Compare commits
385 Commits
Author | SHA1 | Date | |
---|---|---|---|
2df917e7a5 | |||
![]() |
c600b1b39c | ||
![]() |
f4f5731bdc | ||
![]() |
0d53697917 | ||
![]() |
a4006f5046 | ||
![]() |
264ded8101 | ||
![]() |
4a88c520ac | ||
![]() |
761b5b1ef4 | ||
![]() |
257d9e5b78 | ||
![]() |
9cf273d187 | ||
![]() |
cf76a3cbbb | ||
![]() |
e5e006d933 | ||
![]() |
2dadbcfdcb | ||
![]() |
9962fab124 | ||
![]() |
1089a7a07b | ||
![]() |
c56c4a7a9d | ||
![]() |
895415aee1 | ||
![]() |
8800a08150 | ||
![]() |
82806b53e2 | ||
![]() |
3786197ab2 | ||
![]() |
75faa22dff | ||
![]() |
2283af8ce5 | ||
![]() |
00c37b0b5b | ||
![]() |
05658f0850 | ||
![]() |
0af2769aca | ||
![]() |
529f5a1cd0 | ||
![]() |
ce70209310 | ||
![]() |
888073df05 | ||
![]() |
6fa3e19f7e | ||
![]() |
15c55a4ef5 | ||
![]() |
2ec29a4d4c | ||
![]() |
330f155168 | ||
![]() |
fb1c284774 | ||
![]() |
d705fa6ed4 | ||
![]() |
61ec339c2d | ||
![]() |
b6e3f360c9 | ||
![]() |
edf406c172 | ||
![]() |
1ffa0d69cb | ||
![]() |
d5e667d0ee | ||
![]() |
e5deabe0aa | ||
![]() |
59ecf8eb83 | ||
![]() |
72a3459119 | ||
![]() |
9c3a4e7b00 | ||
![]() |
d7c23e1b7f | ||
![]() |
07c286fad5 | ||
![]() |
8555ff4ced | ||
![]() |
c03fbecb4e | ||
![]() |
b36f298815 | ||
![]() |
7064279316 | ||
![]() |
10960564cf | ||
![]() |
0a94e59325 | ||
![]() |
3cc4f5bed3 | ||
![]() |
4495000703 | ||
![]() |
879b62c353 | ||
![]() |
912861c8b0 | ||
![]() |
b1c038e882 | ||
![]() |
a10d724bb1 | ||
![]() |
a5d6dd3001 | ||
![]() |
9a039f0753 | ||
![]() |
6d08882978 | ||
![]() |
9eefe23f4c | ||
![]() |
53730c4ba2 | ||
![]() |
861ddc9ce0 | ||
![]() |
03959b670c | ||
![]() |
ee878fa40a | ||
![]() |
5b78f21921 | ||
![]() |
a2139b95b5 | ||
![]() |
3ae3eae48b | ||
![]() |
9f591b0578 | ||
![]() |
565fb3dc3a | ||
![]() |
bcf7c9e978 | ||
![]() |
7c1cad00b0 | ||
![]() |
bae443dfce | ||
![]() |
d924270bb1 | ||
![]() |
d1f3be0eed | ||
![]() |
6cef3f3966 | ||
![]() |
cd23a035c5 | ||
![]() |
dbb22f6335 | ||
![]() |
31548737c6 | ||
![]() |
994bd72363 | ||
![]() |
d48f5599d3 | ||
![]() |
cfc424667a | ||
![]() |
e8c8515211 | ||
![]() |
c129ce2e4d | ||
![]() |
d54d953af1 | ||
![]() |
32b0c659a1 | ||
![]() |
6a792ed056 | ||
![]() |
bdebfe330a | ||
![]() |
629d02948f | ||
![]() |
da61441bf9 | ||
![]() |
21c951201c | ||
![]() |
b07706fd7a | ||
![]() |
3a176fa658 | ||
![]() |
8689170368 | ||
![]() |
6c8537fab1 | ||
![]() |
c69662eb28 | ||
![]() |
be9e759697 | ||
![]() |
7902979470 | ||
![]() |
552fd062ea | ||
![]() |
aeb04e24e2 | ||
![]() |
686db90032 | ||
![]() |
7a644b6d61 | ||
![]() |
21df9d55bb | ||
![]() |
b79aa05014 | ||
![]() |
68dd013585 | ||
![]() |
54d5c7b35c | ||
![]() |
69d230d065 | ||
![]() |
a93a846687 | ||
![]() |
6ba03ed65b | ||
![]() |
1f6546f484 | ||
![]() |
5c84a0be92 | ||
![]() |
02ced3a639 | ||
![]() |
afa673ff46 | ||
![]() |
f609afddb6 | ||
![]() |
6d424f91a5 | ||
![]() |
955762b71e | ||
![]() |
6ffac092ed | ||
![]() |
e0ce4a8348 | ||
![]() |
2fb94bd3e6 | ||
![]() |
005fed6cdf | ||
![]() |
6d251d5c58 | ||
![]() |
51abb471b9 | ||
![]() |
9b75dd1321 | ||
![]() |
8719e5d74c | ||
![]() |
e4cf07c107 | ||
![]() |
b69f263c10 | ||
![]() |
e62acf815a | ||
![]() |
1447ecad57 | ||
![]() |
1057309951 | ||
![]() |
e6cd559b82 | ||
![]() |
d9202f8d14 | ||
![]() |
6c8df74b7f | ||
![]() |
6681ad79bc | ||
![]() |
8fd4d9e853 | ||
![]() |
ad2a976924 | ||
![]() |
e36dba5acb | ||
![]() |
22cf6df02f | ||
![]() |
ea6f47bf33 | ||
![]() |
1502eb4d59 | ||
![]() |
83308758f0 | ||
![]() |
b629cbfe9e | ||
![]() |
a86c36ceb1 | ||
![]() |
194e0bc3cc | ||
![]() |
fa72d66d5d | ||
![]() |
ce56a4cdf3 | ||
![]() |
5e0df3d2cc | ||
![]() |
9ea58a2ce2 | ||
![]() |
25f1801061 | ||
![]() |
e6e2404d24 | ||
![]() |
ad404bfbe6 | ||
![]() |
eb12a8cfc6 | ||
![]() |
dad208a0d5 | ||
![]() |
723d32b8b7 | ||
![]() |
05b57f4960 | ||
![]() |
452b6c4ae2 | ||
![]() |
514c27d681 | ||
![]() |
b253fdba33 | ||
![]() |
b370eea13c | ||
![]() |
4f0878ccbf | ||
![]() |
97c13e15f0 | ||
![]() |
4d18800bc0 | ||
![]() |
c6e1f0536c | ||
![]() |
e59c863c23 | ||
![]() |
be1d984364 | ||
![]() |
73ba5f805b | ||
![]() |
981a11cee1 | ||
![]() |
9b7667c3a5 | ||
![]() |
c73dcd2786 | ||
![]() |
e24f3535ff | ||
![]() |
4efedd2951 | ||
![]() |
90b18a1ec1 | ||
![]() |
ef47bda417 | ||
![]() |
85b509cedc | ||
![]() |
35d32aa3d7 | ||
![]() |
610902a5c1 | ||
![]() |
c15841c828 | ||
![]() |
1dff6361b7 | ||
![]() |
2768b22494 | ||
![]() |
875866c3c0 | ||
![]() |
34ed18527b | ||
![]() |
be30203d81 | ||
![]() |
40b139ab82 | ||
![]() |
a513b33da3 | ||
![]() |
a72d53ae1b | ||
![]() |
cce22c9517 | ||
![]() |
009e2e6adb | ||
![]() |
1cee27275e | ||
![]() |
c7ad14320f | ||
![]() |
e005836286 | ||
![]() |
849b295384 | ||
![]() |
9192bb0d44 | ||
![]() |
9a5bd57d1b | ||
![]() |
8a780cb7f5 | ||
![]() |
92c3ba578a | ||
![]() |
da9205c78a | ||
![]() |
3abee97e13 | ||
![]() |
86ef6906ad | ||
![]() |
fe209dfd24 | ||
![]() |
daf1fd4759 | ||
![]() |
c34427e264 | ||
![]() |
bf140ae175 | ||
![]() |
19609705fc | ||
![]() |
aef9aa2dd3 | ||
![]() |
0fbc644fd9 | ||
![]() |
f688cba49c | ||
![]() |
7dccbd1ead | ||
![]() |
9690f52dc5 | ||
![]() |
e846f26bd3 | ||
![]() |
54a5dd92cf | ||
![]() |
c32fbe14aa | ||
![]() |
acee13bf53 | ||
![]() |
d446f849d8 | ||
![]() |
b91eace873 | ||
![]() |
0568d2ae3b | ||
![]() |
6ca61dc709 | ||
![]() |
37f92396c4 | ||
![]() |
769e70e76a | ||
![]() |
db32b65d81 | ||
![]() |
7080c0371d | ||
![]() |
e90c8a97ff | ||
![]() |
6cd45f88c5 | ||
![]() |
fe6846d5eb | ||
![]() |
ae7656e348 | ||
![]() |
e85363da20 | ||
![]() |
db8aa6cf15 | ||
![]() |
d9b37eec6c | ||
![]() |
3eed63b892 | ||
![]() |
fbb58918af | ||
![]() |
ef1ab34234 | ||
![]() |
577944c8fb | ||
![]() |
457b275800 | ||
![]() |
32a4269f4a | ||
![]() |
d316624f61 | ||
![]() |
8c7a225070 | ||
![]() |
6c7c25d9e7 | ||
![]() |
17627cdf3c | ||
![]() |
6b315b1dad | ||
![]() |
30aac6b6a8 | ||
![]() |
60488d4439 | ||
![]() |
4d4e7cc5e9 | ||
![]() |
dc9e3c9c11 | ||
![]() |
01f357ae3a | ||
![]() |
21bec492e8 | ||
![]() |
c629529565 | ||
![]() |
c4cf6b646b | ||
![]() |
f8d23d0d64 | ||
![]() |
7b582e0339 | ||
![]() |
888db2f7cf | ||
![]() |
f7eaa7bec2 | ||
![]() |
85effc2946 | ||
![]() |
b5101d11d6 | ||
![]() |
c9d1f7c98d | ||
![]() |
4694f5f52f | ||
![]() |
fdc00ad678 | ||
![]() |
0607d9021a | ||
![]() |
ad28f4df62 | ||
![]() |
3acfb8e4b3 | ||
![]() |
e0c875a367 | ||
![]() |
6c7443223b | ||
![]() |
c87d6c5cb7 | ||
![]() |
b19ba4dc2d | ||
![]() |
01833c698c | ||
![]() |
199d12636b | ||
![]() |
ee3950a3aa | ||
![]() |
f8d8368b10 | ||
![]() |
6e4d39c7b3 | ||
![]() |
c177ae2c7a | ||
![]() |
bc29af7c0c | ||
![]() |
466acad6fb | ||
![]() |
8c0643092b | ||
![]() |
2198986d5c | ||
![]() |
fc2e8264d4 | ||
![]() |
74afb8a7cc | ||
![]() |
fa8ebeb42d | ||
![]() |
ae0cad589e | ||
![]() |
d9d48e8948 | ||
![]() |
f66be6c943 | ||
![]() |
6921400a7a | ||
![]() |
cb6a52a652 | ||
![]() |
4a556dcab1 | ||
![]() |
d9a2a66073 | ||
![]() |
55f67d602e | ||
![]() |
ff02e0da90 | ||
![]() |
1430861c6b | ||
![]() |
8a9d3de1ef | ||
![]() |
8efde55755 | ||
![]() |
7f55410a02 | ||
![]() |
6590931fcd | ||
![]() |
4d94309087 | ||
![]() |
4d3a7ad5c6 | ||
![]() |
f10442cc78 | ||
![]() |
8f52c83f0b | ||
![]() |
4e498c90e6 | ||
![]() |
520e250966 | ||
![]() |
0c288aa355 | ||
![]() |
4a7c2e2d42 | ||
![]() |
c8ce468c7f | ||
![]() |
63e74a01d3 | ||
![]() |
63dffb48c5 | ||
![]() |
867baa7266 | ||
![]() |
0e733bd0de | ||
![]() |
71342f4bd2 | ||
![]() |
55f9ca5996 | ||
![]() |
7d0b2479f5 | ||
![]() |
0d69e8fe3e | ||
![]() |
5f030b0033 | ||
![]() |
4e1f6b5bd5 | ||
![]() |
7d9751048e | ||
![]() |
c88a2dceb9 | ||
![]() |
730db56623 | ||
![]() |
ccd8021af9 | ||
![]() |
079d0b1060 | ||
![]() |
c2c34528a2 | ||
![]() |
f795e58483 | ||
![]() |
c85fc7a098 | ||
![]() |
5b4b4190a5 | ||
![]() |
e062c431dd | ||
![]() |
a2da1c9e8d | ||
![]() |
7fdff6a735 | ||
![]() |
c752353471 | ||
![]() |
5e6d3a273d | ||
![]() |
5f1e7a77e6 | ||
![]() |
fddeb4a23c | ||
![]() |
f056e0341e | ||
![]() |
2a14ae9c0b | ||
![]() |
20051e18c9 | ||
![]() |
c3331645e4 | ||
![]() |
cc7075ab65 | ||
![]() |
5e3fbb1623 | ||
![]() |
fcd084f908 | ||
![]() |
4ce5e04d7e | ||
![]() |
8acd40905d | ||
![]() |
1288a13520 | ||
![]() |
7ce62cbd98 | ||
![]() |
07de8f7515 | ||
![]() |
7e80d203ff | ||
![]() |
a5650d3251 | ||
![]() |
f2b5cf1cc0 | ||
![]() |
4cab815317 | ||
![]() |
3f29b411d0 | ||
![]() |
0d75af86d8 | ||
![]() |
ed363452c9 | ||
![]() |
50eb23d60e | ||
![]() |
b40008a06d | ||
![]() |
febac40b05 | ||
![]() |
59d9dc0307 | ||
![]() |
2a08357fa4 | ||
![]() |
d43a421dee | ||
![]() |
f05f214f6c | ||
![]() |
d6f0cfcbad | ||
![]() |
6cf84a7319 | ||
![]() |
5d5a33f90b | ||
![]() |
c457b485cb | ||
![]() |
5a7813c05c | ||
![]() |
5035fc7eed | ||
![]() |
2866196dad | ||
![]() |
3c151ca509 | ||
![]() |
51c736a92c | ||
![]() |
18d079a99a | ||
![]() |
b07475efea | ||
![]() |
04816e014b | ||
![]() |
662813dc18 | ||
![]() |
735788dbd6 | ||
![]() |
e76007fe20 | ||
![]() |
9ea5790a9b | ||
![]() |
5645b4c307 | ||
![]() |
61eb2e7764 | ||
![]() |
d031126737 | ||
![]() |
4449d51d98 | ||
![]() |
a28e6e1044 | ||
![]() |
04770e4037 | ||
![]() |
af66472c36 | ||
![]() |
1d017449d0 | ||
![]() |
6efbbb88da | ||
![]() |
0ba692c85a | ||
![]() |
6e47f12118 | ||
![]() |
9a1c071f40 | ||
![]() |
29d94c6478 | ||
![]() |
76e3a34e7c | ||
![]() |
0eaae9790c | ||
![]() |
43770dae36 | ||
![]() |
d08831cc7e | ||
![]() |
6d2755abe6 | ||
![]() |
7bdf7b3f36 | ||
![]() |
9215c09656 |
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
rpm/
|
||||
pkgs/
|
||||
|
13
.travis.yml
Normal file
@ -0,0 +1,13 @@
|
||||
sudo: required
|
||||
dist: bionic
|
||||
language: generic
|
||||
install: git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
|
||||
script: ~/qubes-builder/scripts/travis-build
|
||||
env:
|
||||
- DIST_DOM0=fc31 USE_QUBES_REPO_VERSION=4.1 USE_QUBES_REPO_TESTING=1
|
||||
|
||||
# don't build tags which are meant for code signing only
|
||||
branches:
|
||||
except:
|
||||
- /.*_.*/
|
||||
- build
|
@ -1,5 +1,4 @@
|
||||
This package contains Linux dom0 files, which aren't exactly part of "Qubes
|
||||
core" i.e. aren't required for normal Qubes usage.
|
||||
This package contains Linux dom0 files.
|
||||
|
||||
Mostly there are config files for different system services, but also some
|
||||
"addons" to Qubes core:
|
||||
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Command Prompt
|
||||
Comment=Use the command line
|
||||
Categories=GNOME;GTK;Utility;TerminalEmulator;System;
|
||||
Exec=cmd /c start cmd
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Explorer
|
||||
Comment=Browse files
|
||||
Categories=Utility;Core;
|
||||
Exec=explorer
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Internet Explorer
|
||||
Comment=Browse the Web
|
||||
Categories=Network;WebBrowser;
|
||||
Exec=C:\\Program Files\\Internet Explorer\\iexplore.exe
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=qubes-vm-settings %VMNAME% applications
|
||||
Icon=qubes-logo
|
||||
Terminal=false
|
||||
Name=%VMNAME%: Add more shortcuts...
|
||||
GenericName=%VMNAME%: Add more shortcuts...
|
||||
StartupNotify=false
|
||||
Categories=System;
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=sh -c 'echo firefox | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
|
||||
Icon=dispvm-red
|
||||
Terminal=false
|
||||
Name=DispVM: Firefox web browser
|
||||
GenericName=DispVM: Web browser
|
||||
StartupNotify=false
|
||||
Categories=Network;
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=DisposableVM
|
||||
Icon=dispvm-red
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=ServiceVM: %VMNAME%
|
||||
Icon=%XDGICON%
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=qvm-start --quiet --tray %VMNAME%
|
||||
Icon=%XDGICON%
|
||||
Terminal=false
|
||||
Name=%VMNAME%: Start
|
||||
GenericName=%VMNAME%: Start
|
||||
StartupNotify=false
|
||||
Categories=System;
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=Template: %VMNAME%
|
||||
Icon=qubes-logo
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=Domain: %VMNAME%
|
||||
Icon=%XDGICON%
|
@ -1,16 +0,0 @@
|
||||
#!/bin/sh
|
||||
SRC=$1
|
||||
DSTDIR=$2
|
||||
VMNAME=$3
|
||||
VMDIR=$4
|
||||
XDGICON=$5
|
||||
|
||||
DST=$DSTDIR/$VMNAME-$(basename $SRC)
|
||||
|
||||
sed \
|
||||
-e "s/%VMNAME%/$VMNAME/" \
|
||||
-e "s %VMDIR% $VMDIR " \
|
||||
-e "s/%XDGICON%/$XDGICON/" \
|
||||
<$SRC >$DST
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
SRC=$1
|
||||
DST=$2
|
||||
VMNAME=$3
|
||||
VMDIR=$4
|
||||
XDGICON=$5
|
||||
|
||||
sed \
|
||||
-e "s/%VMNAME%/$VMNAME/" \
|
||||
-e "s %VMDIR% $VMDIR " \
|
||||
-e "s/%XDGICON%/$XDGICON/" \
|
||||
<$SRC >$DST
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
SRCDIR=$1
|
||||
VMNAME=$2
|
||||
VMTYPE=$3
|
||||
if [ -z "$VMTYPE" ]; then
|
||||
VMTYPE=appvms
|
||||
fi
|
||||
XDGICON=$4
|
||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
||||
APPSDIR=$VMDIR/apps
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "usage: $0 <apps_templates_dir> <vmname> [appvms|vm-templates|servicevms]"
|
||||
exit
|
||||
fi
|
||||
mkdir -p $APPSDIR
|
||||
|
||||
if [ "$SRCDIR" != "none" ]; then
|
||||
echo "--> Converting Appmenu Templates..."
|
||||
if [ -r "$VMDIR/whitelisted-appmenus.list" ]; then
|
||||
cat $VMDIR/whitelisted-appmenus.list | xargs -I{} /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh $SRCDIR/{} $APPSDIR $VMNAME $VMDIR $XDGICON
|
||||
else
|
||||
find $SRCDIR -name "*.desktop" $CHECK_WHITELISTED -exec /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh {} $APPSDIR $VMNAME $VMDIR $XDGICON \;
|
||||
fi
|
||||
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh /usr/share/qubes-appmenus/qubes-appmenu-select.desktop $APPSDIR $VMNAME $VMDIR $XDGICON
|
||||
|
||||
if [ "$VMTYPE" = "vm-templates" ]; then
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-templatevm.directory.template
|
||||
elif [ "$VMTYPE" = "servicevms" ]; then
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-servicevm.directory.template
|
||||
else
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-vm.directory.template
|
||||
fi
|
||||
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh $DIR_TEMPLATE $APPSDIR/$VMNAME-vm.directory $VMNAME $VMDIR $XDGICON
|
||||
fi
|
||||
|
||||
echo "--> Adding Apps to the Menu..."
|
||||
LC_COLLATE=C xdg-desktop-menu install $APPSDIR/*.directory $APPSDIR/*.desktop
|
||||
|
||||
if [ -n "$KDE_SESSION_UID" ]; then
|
||||
kbuildsycoca4
|
||||
fi
|
@ -1,276 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import dbus
|
||||
|
||||
from qubes.qubes import QubesVm,QubesHVm
|
||||
from qubes.qubes import QubesException,QubesHost,QubesVmLabels
|
||||
from qubes.qubes import vm_files,system_path,dry_run
|
||||
|
||||
import qubes.imgconverter
|
||||
|
||||
vm_files['appmenus_templates_subdir'] = 'apps.templates'
|
||||
vm_files['appmenus_template_icons_subdir'] = 'apps.tempicons'
|
||||
vm_files['appmenus_icons_subdir'] = 'apps.icons'
|
||||
vm_files['appmenus_template_templates_subdir'] = 'apps-template.templates'
|
||||
vm_files['appmenus_whitelist'] = 'whitelisted-appmenus.list'
|
||||
|
||||
system_path['appmenu_start_hvm_template'] = '/usr/share/qubes-appmenus/qubes-start.desktop'
|
||||
system_path['appmenu_create_cmd'] = '/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh'
|
||||
system_path['appmenu_remove_cmd'] = '/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh'
|
||||
|
||||
|
||||
def QubesVm_get_attrs_config(self, attrs):
|
||||
attrs["appmenus_templates_dir"] = { "eval": \
|
||||
'os.path.join(self.dir_path, vm_files["appmenus_templates_subdir"]) if self.updateable else ' + \
|
||||
'self.template.appmenus_templates_dir if self.template is not None else None' }
|
||||
attrs["appmenus_template_icons_dir"] = { "eval": \
|
||||
'os.path.join(self.dir_path, vm_files["appmenus_template_icons_subdir"]) if self.updateable else ' + \
|
||||
'self.template.appmenus_template_icons_dir if self.template is not None else None' }
|
||||
attrs["appmenus_icons_dir"] = { "eval": \
|
||||
'os.path.join(self.dir_path, vm_files["appmenus_icons_subdir"])' }
|
||||
return attrs
|
||||
|
||||
def QubesTemplateVm_get_attrs_config(self, attrs):
|
||||
attrs['appmenus_templates_dir'] = { 'eval': 'os.path.join(self.dir_path, vm_files["appmenus_templates_subdir"])' }
|
||||
attrs['appmenus_template_icons_dir'] = { 'eval': 'os.path.join(self.dir_path, vm_files["appmenus_template_icons_subdir"])' }
|
||||
return attrs
|
||||
|
||||
def QubesVm_appmenus_create(self, verbose=False, source_template = None):
|
||||
if source_template is None:
|
||||
source_template = self.template
|
||||
|
||||
if self.internal:
|
||||
return
|
||||
|
||||
vmtype = None
|
||||
if self.is_netvm():
|
||||
vmtype = 'servicevms'
|
||||
elif self.is_template():
|
||||
vmtype = 'vm-templates'
|
||||
else:
|
||||
vmtype = 'appvms'
|
||||
|
||||
try:
|
||||
msgoutput = None if verbose else open(os.devnull, 'w')
|
||||
if source_template is not None:
|
||||
subprocess.check_call([system_path["appmenu_create_cmd"],
|
||||
source_template.appmenus_templates_dir,
|
||||
self.name, vmtype, self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
elif self.appmenus_templates_dir is not None:
|
||||
subprocess.check_call ([system_path["appmenu_create_cmd"],
|
||||
self.appmenus_templates_dir, self.name,
|
||||
vmtype, self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
else:
|
||||
# Only add apps to menu
|
||||
subprocess.check_call ([system_path["appmenu_create_cmd"],
|
||||
"none", self.name, vmtype,
|
||||
self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
except subprocess.CalledProcessError:
|
||||
print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name)
|
||||
|
||||
def QubesVm_appmenus_remove(self):
|
||||
vmtype = None
|
||||
if self.is_netvm():
|
||||
vmtype = 'servicevms'
|
||||
elif self.is_template():
|
||||
vmtype = 'vm-templates'
|
||||
else:
|
||||
vmtype = 'appvms'
|
||||
subprocess.check_call ([system_path["appmenu_remove_cmd"], self.name,
|
||||
vmtype], stderr=open(os.devnull, 'w'))
|
||||
|
||||
def QubesVm_appicons_create(self, srcdir=None):
|
||||
if srcdir is None:
|
||||
srcdir = self.appmenus_template_icons_dir
|
||||
if srcdir is None:
|
||||
return
|
||||
if not os.path.exists(srcdir):
|
||||
return
|
||||
|
||||
whitelist = os.path.join(self.dir_path, vm_files['appmenus_whitelist'])
|
||||
if os.path.exists(whitelist):
|
||||
whitelist = [line.strip() for line in open(whitelist)]
|
||||
else:
|
||||
whitelist = None
|
||||
|
||||
if not os.path.exists(self.appmenus_icons_dir):
|
||||
os.mkdir(self.appmenus_icons_dir)
|
||||
elif not os.path.isdir(self.appmenus_icons_dir):
|
||||
os.unlink(self.appmenus_icons_dir)
|
||||
os.mkdir(self.appmenus_icons_dir)
|
||||
|
||||
for icon in os.listdir(srcdir):
|
||||
desktop = os.path.splitext(icon)[0] + '.desktop'
|
||||
if whitelist and desktop not in whitelist:
|
||||
continue
|
||||
|
||||
qubes.imgconverter.tint(os.path.join(srcdir, icon),
|
||||
os.path.join(self.appmenus_icons_dir, icon),
|
||||
self.label.color)
|
||||
|
||||
def QubesVm_appicons_remove(self):
|
||||
if not os.path.exists(self.appmenus_icons_dir): return
|
||||
for icon in os.listdir(self.appmenus_icons_dir):
|
||||
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
|
||||
|
||||
def QubesVm_pre_rename(self, new_name):
|
||||
self.appmenus_remove()
|
||||
|
||||
def QubesVm_post_rename(self, old_name):
|
||||
old_dirpath = os.path.join(os.path.dirname(self.dir_path), old_name)
|
||||
if self.appmenus_templates_dir is not None:
|
||||
self.appmenus_templates_dir = self.appmenus_templates_dir.replace(old_dirpath, self.dir_path)
|
||||
|
||||
self.appmenus_create()
|
||||
|
||||
def QubesVm_create_on_disk(self, verbose, source_template):
|
||||
if isinstance(self, QubesHVm) and source_template is None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating appmenus directory: {0}".format(self.appmenus_templates_dir)
|
||||
os.mkdir (self.appmenus_templates_dir)
|
||||
shutil.copy (system_path["appmenu_start_hvm_template"], self.appmenus_templates_dir)
|
||||
|
||||
source_whitelist_filename = 'vm-' + vm_files["appmenus_whitelist"]
|
||||
if self.is_netvm():
|
||||
source_whitelist_filename = 'netvm-' + vm_files["appmenus_whitelist"]
|
||||
if source_template and os.path.exists(os.path.join(source_template.dir_path, source_whitelist_filename)):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating default whitelisted apps list: {0}".\
|
||||
format(self.dir_path + '/' + vm_files["whitelisted_appmenus"])
|
||||
shutil.copy(os.path.join(source_template.dir_path, source_whitelist_filename),
|
||||
os.path.join(self.dir_path, vm_files["whitelisted_appmenus"]))
|
||||
|
||||
if source_template and self.updateable:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
||||
format(source_template.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
if os.path.isdir(source_template.appmenus_templates_dir):
|
||||
shutil.copytree (source_template.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
else:
|
||||
os.mkdir(self.appmenus_templates_dir)
|
||||
if os.path.isdir(source_template.appmenus_template_icons_dir):
|
||||
shutil.copytree (source_template.appmenus_template_icons_dir, self.appmenus_template_icons_dir)
|
||||
else:
|
||||
os.mkdir(self.appmenus_template_icons_dir)
|
||||
|
||||
# Create appmenus
|
||||
self.appicons_create()
|
||||
self.appmenus_create(verbose=verbose)
|
||||
|
||||
def QubesVm_clone_disk_files(self, src_vm, verbose):
|
||||
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and self.appmenus_templates_dir is not None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
||||
format(src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
shutil.copytree (src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
||||
|
||||
if src_vm.updateable and src_vm.appmenus_template_icons_dir is not None \
|
||||
and self.appmenus_template_icons_dir is not None and \
|
||||
os.path.isdir(src_vm.appmenus_template_icons_dir):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus " \
|
||||
"template icons dir:\n{0} ==>\n{1}".\
|
||||
format(src_vm.appmenus_template_icons_dir,
|
||||
self.appmenus_template_icons_dir)
|
||||
shutil.copytree (src_vm.appmenus_template_icons_dir,
|
||||
self.appmenus_template_icons_dir)
|
||||
|
||||
for whitelist in (
|
||||
vm_files["appmenus_whitelist"],
|
||||
'vm-' + vm_files["appmenus_whitelist"],
|
||||
'netvm-' + vm_files["appmenus_whitelist"]):
|
||||
if os.path.exists(os.path.join(src_vm.dir_path, whitelist)):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\
|
||||
format(whitelist)
|
||||
shutil.copy(os.path.join(src_vm.dir_path, whitelist),
|
||||
os.path.join(self.dir_path, whitelist))
|
||||
|
||||
# Create appmenus
|
||||
self.appicons_create()
|
||||
self.appmenus_create(verbose=verbose)
|
||||
|
||||
def QubesVm_remove_from_disk(self):
|
||||
self.appmenus_remove()
|
||||
|
||||
def QubesVm_label_setter(self, _):
|
||||
self.appicons_create()
|
||||
|
||||
# Apparently desktop environments heavily caches the icons,
|
||||
# see #751 for details
|
||||
if os.environ.get("DESKTOP_SESSION", "") == "kde-plasma":
|
||||
try:
|
||||
os.unlink(os.path.expandvars("$HOME/.kde/cache-$HOSTNAME/icon-cache.kcache"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
notify_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
|
||||
notify_object.Notify(
|
||||
"Qubes", 0, self.label.icon, "Qubes",
|
||||
"You will need to log off and log in again for the VM icons to update in the KDE launcher menu",
|
||||
[], [], 10000,
|
||||
dbus_interface="org.freedesktop.Notifications")
|
||||
except:
|
||||
pass
|
||||
elif os.environ.get("DESKTOP_SESSION", "") == "xfce":
|
||||
self.appmenus_remove()
|
||||
self.appmenus_create()
|
||||
|
||||
def QubesVm_appmenus_recreate(self):
|
||||
self.appmenus_remove()
|
||||
self.appicons_remove()
|
||||
self.appicons_create()
|
||||
self.appmenus_create()
|
||||
|
||||
def QubesVm_set_attr(self, name, newvalue, oldvalue):
|
||||
if name == 'internal':
|
||||
if newvalue and not oldvalue:
|
||||
self.appmenus_remove()
|
||||
elif not newvalue and oldvalue:
|
||||
self.appmenus_create()
|
||||
|
||||
# new methods
|
||||
QubesVm.appmenus_create = QubesVm_appmenus_create
|
||||
QubesVm.appmenus_remove = QubesVm_appmenus_remove
|
||||
QubesVm.appmenus_recreate = QubesVm_appmenus_recreate
|
||||
QubesVm.appicons_create = QubesVm_appicons_create
|
||||
QubesVm.appicons_remove = QubesVm_appicons_remove
|
||||
|
||||
# hooks for existing methods
|
||||
QubesVm.hooks_get_attrs_config.append(QubesVm_get_attrs_config)
|
||||
QubesVm.hooks_pre_rename.append(QubesVm_pre_rename)
|
||||
QubesVm.hooks_post_rename.append(QubesVm_post_rename)
|
||||
QubesVm.hooks_create_on_disk.append(QubesVm_create_on_disk)
|
||||
QubesVm.hooks_clone_disk_files.append(QubesVm_clone_disk_files)
|
||||
QubesVm.hooks_remove_from_disk.append(QubesVm_remove_from_disk)
|
||||
QubesVm.hooks_label_setter.append(QubesVm_label_setter)
|
||||
QubesVm.hooks_set_attr.append(QubesVm_set_attr)
|
@ -1,334 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import pipes
|
||||
|
||||
from optparse import OptionParser
|
||||
from qubes.qubes import QubesVmCollection,QubesException,system_path
|
||||
from qubes.qubes import QubesHVm
|
||||
from qubes.qubes import vm_files
|
||||
import qubes.imgconverter
|
||||
|
||||
# fields required to be present (and verified) in retrieved desktop file
|
||||
required_fields = [ "Name", "Exec" ]
|
||||
|
||||
#limits
|
||||
appmenus_line_size = 1024
|
||||
appmenus_line_count = 100000
|
||||
|
||||
# regexps for sanitization of retrieved values
|
||||
std_re = re.compile(r"^[/a-zA-Z0-9.,&() -]*$")
|
||||
fields_regexp = {
|
||||
"Name": std_re,
|
||||
"GenericName": std_re,
|
||||
"Comment": std_re,
|
||||
"Categories": re.compile(r"^[a-zA-Z0-9/.;:'() -]*$"),
|
||||
"Exec": re.compile(r"^[a-zA-Z0-9()%&>/{}\"'\\:.= -]*$"),
|
||||
"Icon": re.compile(r"^[a-zA-Z0-9/_.-]*$"),
|
||||
}
|
||||
|
||||
CATEGORIES_WHITELIST = set([
|
||||
# Main Categories
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apa.html 20140507
|
||||
'AudioVideo', 'Audio', 'Video', 'Development', 'Education', 'Game',
|
||||
'Graphics', 'Network', 'Office', 'Science', 'Settings', 'System',
|
||||
'Utility',
|
||||
|
||||
# Additional Categories
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apas02.html
|
||||
'Building', 'Debugger', 'IDE', 'GUIDesigner', 'Profiling',
|
||||
'RevisionControl', 'Translation', 'Calendar', 'ContactManagement',
|
||||
'Database', 'Dictionary', 'Chart', 'Email', 'Finance', 'FlowChart', 'PDA',
|
||||
'ProjectManagement', 'Presentation', 'Spreadsheet', 'WordProcessor',
|
||||
'2DGraphics', 'VectorGraphics', 'RasterGraphics', '3DGraphics', 'Scanning',
|
||||
'OCR', 'Photography', 'Publishing', 'Viewer', 'TextTools',
|
||||
'DesktopSettings', 'HardwareSettings', 'Printing', 'PackageManager',
|
||||
'Dialup', 'InstantMessaging', 'Chat', 'IRCClient', 'Feed', 'FileTransfer',
|
||||
'HamRadio', 'News', 'P2P', 'RemoteAccess', 'Telephony', 'TelephonyTools',
|
||||
'VideoConference', 'WebBrowser', 'WebDevelopment', 'Midi', 'Mixer',
|
||||
'Sequencer', 'Tuner', 'TV', 'AudioVideoEditing', 'Player', 'Recorder',
|
||||
'DiscBurning', 'ActionGame', 'AdventureGame', 'ArcadeGame', 'BoardGame',
|
||||
'BlocksGame', 'CardGame', 'KidsGame', 'LogicGame', 'RolePlaying',
|
||||
'Shooter', 'Simulation', 'SportsGame', 'StrategyGame', 'Art',
|
||||
'Construction', 'Music', 'Languages', 'ArtificialIntelligence',
|
||||
'Astronomy', 'Biology', 'Chemistry', 'ComputerScience',
|
||||
'DataVisualization', 'Economy', 'Electricity', 'Geography', 'Geology',
|
||||
'Geoscience', 'History', 'Humanities', 'ImageProcessing', 'Literature',
|
||||
'Maps', 'Math', 'NumericalAnalysis', 'MedicalSoftware', 'Physics',
|
||||
'Robotics', 'Spirituality', 'Sports', 'ParallelComputing', 'Amusement',
|
||||
'Archiving', 'Compression', 'Electronics', 'Emulator', 'Engineering',
|
||||
'FileTools', 'FileManager', 'TerminalEmulator', 'Filesystem', 'Monitor',
|
||||
'Security', 'Accessibility', 'Calculator', 'Clock', 'TextEditor',
|
||||
'Documentation', 'Adult', 'Core', 'KDE', 'GNOME', 'XFCE', 'GTK', 'Qt',
|
||||
'Motif', 'Java', 'ConsoleOnly',
|
||||
|
||||
# Reserved Categories (not whitelisted)
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apas03.html
|
||||
# 'Screensaver', 'TrayIcon', 'Applet', 'Shell',
|
||||
])
|
||||
|
||||
def sanitise_categories(untrusted_value):
|
||||
untrusted_categories = (c.strip() for c in untrusted_value.split(';') if c)
|
||||
categories = (c for c in untrusted_categories if c in CATEGORIES_WHITELIST)
|
||||
|
||||
return ';'.join(categories) + ';'
|
||||
|
||||
def fallback_hvm_appmenulist():
|
||||
p = subprocess.Popen(["grep", "-rH", "=", "/usr/share/qubes-appmenus/hvm"],
|
||||
stdout=subprocess.PIPE)
|
||||
(stdout, stderr) = p.communicate()
|
||||
return stdout.splitlines()
|
||||
|
||||
def get_appmenus(vm):
|
||||
global appmenus_line_count
|
||||
global appmenus_line_size
|
||||
untrusted_appmenulist = []
|
||||
if vm is None:
|
||||
while appmenus_line_count > 0:
|
||||
untrusted_line = sys.stdin.readline(appmenus_line_size)
|
||||
if untrusted_line == "":
|
||||
break
|
||||
untrusted_appmenulist.append(untrusted_line.strip())
|
||||
appmenus_line_count -= 1
|
||||
if appmenus_line_count == 0:
|
||||
raise QubesException("Line count limit exceeded")
|
||||
else:
|
||||
p = vm.run('QUBESRPC qubes.GetAppmenus dom0', passio_popen=True)
|
||||
while appmenus_line_count > 0:
|
||||
untrusted_line = p.stdout.readline(appmenus_line_size)
|
||||
if untrusted_line == "":
|
||||
break
|
||||
untrusted_appmenulist.append(untrusted_line.strip())
|
||||
appmenus_line_count -= 1
|
||||
p.wait()
|
||||
if p.returncode != 0:
|
||||
if isinstance(vm, QubesHVm):
|
||||
untrusted_appmenulist = fallback_hvm_appmenulist()
|
||||
else:
|
||||
raise QubesException("Error getting application list")
|
||||
if appmenus_line_count == 0:
|
||||
raise QubesException("Line count limit exceeded")
|
||||
|
||||
row_no = 0
|
||||
appmenus = {}
|
||||
line_rx = re.compile(r"([a-zA-Z0-9.()_-]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)")
|
||||
ignore_rx = re.compile(r".*([a-zA-Z0-9._-]+.desktop):(#.*|\s+)$")
|
||||
for untrusted_line in untrusted_appmenulist:
|
||||
# Ignore blank lines and comments
|
||||
if len(untrusted_line) == 0 or ignore_rx.match(untrusted_line):
|
||||
continue
|
||||
# use search instead of match to skip file path
|
||||
untrusted_m = line_rx.search(untrusted_line)
|
||||
if untrusted_m:
|
||||
filename = untrusted_m.group(1)
|
||||
assert '/' not in filename
|
||||
assert '\0' not in filename
|
||||
|
||||
untrusted_key = untrusted_m.group(2)
|
||||
assert '\0' not in untrusted_key
|
||||
assert '\x1b' not in untrusted_key
|
||||
assert '=' not in untrusted_key
|
||||
|
||||
untrusted_value = untrusted_m.group(3)
|
||||
# TODO add key-dependent asserts
|
||||
|
||||
# Look only at predefined keys
|
||||
if fields_regexp.has_key(untrusted_key):
|
||||
if fields_regexp[untrusted_key].match(untrusted_value):
|
||||
# now values are sanitized
|
||||
key = untrusted_key
|
||||
if key == 'Categories':
|
||||
value = sanitise_categories(untrusted_value)
|
||||
else:
|
||||
value = untrusted_value
|
||||
|
||||
if not appmenus.has_key(filename):
|
||||
appmenus[filename] = {}
|
||||
|
||||
appmenus[filename][key]=value
|
||||
else:
|
||||
print >>sys.stderr, "Warning: ignoring key %r of %s" % (untrusted_key, filename)
|
||||
# else: ignore this key
|
||||
|
||||
return appmenus
|
||||
|
||||
|
||||
def create_template(path, values):
|
||||
# check if all required fields are present
|
||||
for key in required_fields:
|
||||
if not values.has_key(key):
|
||||
print >>sys.stderr, "Warning: not creating/updating '%s' because of missing '%s' key" % (path, key)
|
||||
return
|
||||
|
||||
desktop_file = open(path, "w")
|
||||
desktop_file.write("[Desktop Entry]\n")
|
||||
desktop_file.write("Version=1.0\n")
|
||||
desktop_file.write("Type=Application\n")
|
||||
desktop_file.write("Terminal=false\n")
|
||||
desktop_file.write("X-Qubes-VmName=%VMNAME%\n")
|
||||
|
||||
if 'Icon' in values:
|
||||
icon_file = os.path.splitext(os.path.split(path)[1])[0] + '.png'
|
||||
desktop_file.write("Icon={0}\n".format(os.path.join(
|
||||
'%VMDIR%', vm_files['appmenus_icons_subdir'], icon_file)))
|
||||
else:
|
||||
desktop_file.write("Icon=%XDGICON%\n")
|
||||
|
||||
for key in ["Name", "GenericName" ]:
|
||||
if values.has_key(key):
|
||||
desktop_file.write("{0}=%VMNAME%: {1}\n".format(key, values[key]))
|
||||
|
||||
for key in [ "Comment", "Categories" ]:
|
||||
if values.has_key(key):
|
||||
desktop_file.write("{0}={1}\n".format(key, values[key]))
|
||||
|
||||
desktop_file.write("Exec=qvm-run -q --tray -a %VMNAME% {0}\n".format(pipes.quote(values['Exec'])))
|
||||
desktop_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN")
|
||||
usage = "usage: %prog [options] <vm-name>\n"\
|
||||
"Updates desktop file templates for given StandaloneVM or TemplateVM"
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-v", "--verbose", action="store_true", dest="verbose", default=False)
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
parser.add_option ("--force-rpc", action="store_true", dest="force_rpc", default=False,
|
||||
help="Force to start a new RPC call, even if called from existing one")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 1) and env_vmname is None:
|
||||
parser.error ("You must specify at least the VM name!")
|
||||
|
||||
if env_vmname:
|
||||
vmname=env_vmname
|
||||
else:
|
||||
vmname=args[0]
|
||||
|
||||
if os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
|
||||
if vm is None:
|
||||
print >>sys.stderr, "ERROR: A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if vm.template is not None:
|
||||
print >>sys.stderr, "ERROR: To sync appmenus for template based VM, do it on template instead"
|
||||
exit(1)
|
||||
|
||||
if not vm.is_running():
|
||||
print >>sys.stderr, "ERROR: Appmenus can be retrieved only from running VM - start it first"
|
||||
exit(1)
|
||||
|
||||
new_appmenus = {}
|
||||
if env_vmname is None or options.force_rpc:
|
||||
new_appmenus = get_appmenus(vm)
|
||||
else:
|
||||
options.verbose = False
|
||||
new_appmenus = get_appmenus(None)
|
||||
|
||||
if len(new_appmenus) == 0:
|
||||
print >>sys.stderr, "ERROR: No appmenus received, terminating"
|
||||
exit(1)
|
||||
|
||||
if not os.path.exists(vm.appmenus_templates_dir):
|
||||
os.mkdir(vm.appmenus_templates_dir)
|
||||
|
||||
if not os.path.exists(vm.appmenus_template_icons_dir):
|
||||
os.mkdir(vm.appmenus_template_icons_dir)
|
||||
|
||||
# Create new/update existing templates
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format(str(len(new_appmenus)))
|
||||
for appmenu_file in new_appmenus.keys():
|
||||
if options.verbose:
|
||||
if os.path.exists(os.path.join(vm.appmenus_templates_dir, appmenu_file)):
|
||||
print >> sys.stderr, "---> Updating {0}".format(appmenu_file)
|
||||
else:
|
||||
print >> sys.stderr, "---> Creating {0}".format(appmenu_file)
|
||||
|
||||
if 'Icon' in new_appmenus[appmenu_file]:
|
||||
# the following line is used for time comparison
|
||||
# del new_appmenus[appmenu_file]['Icon']
|
||||
|
||||
icondest = os.path.join(vm.appmenus_template_icons_dir,
|
||||
os.path.splitext(appmenu_file)[0] + '.png')
|
||||
|
||||
try:
|
||||
qubes.imgconverter.Image.get_xdg_icon_from_vm(vm,
|
||||
new_appmenus[appmenu_file]['Icon']).save(icondest)
|
||||
except Exception, e:
|
||||
print >> sys.stderr, '----> Failed to get icon for {0}: {1!s}'.format(appmenu_file, e)
|
||||
|
||||
if os.path.exists(icondest):
|
||||
print >> sys.stderr, '-----> Found old icon, using it instead'
|
||||
else:
|
||||
del new_appmenus[appmenu_file]['Icon']
|
||||
|
||||
create_template(os.path.join(vm.appmenus_templates_dir, appmenu_file),
|
||||
new_appmenus[appmenu_file])
|
||||
|
||||
# Delete appmenus of removed applications
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "--> Cleaning old files"
|
||||
for appmenu_file in os.listdir(vm.appmenus_templates_dir):
|
||||
if not appmenu_file.endswith('.desktop'):
|
||||
continue
|
||||
|
||||
if not new_appmenus.has_key(appmenu_file):
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "---> Removing {0}".format(appmenu_file)
|
||||
os.unlink(os.path.join(vm.appmenus_templates_dir, appmenu_file))
|
||||
|
||||
if isinstance(vm, QubesHVm):
|
||||
if not os.path.exists(os.path.join(vm.appmenus_templates_dir,
|
||||
os.path.basename(system_path['appmenu_start_hvm_template']))):
|
||||
shutil.copy(system_path['appmenu_start_hvm_template'], vm.appmenus_templates_dir)
|
||||
|
||||
if hasattr(vm, 'appvms'):
|
||||
for child_vm in vm.appvms.values():
|
||||
try:
|
||||
child_vm.appmenus_recreate()
|
||||
except Exception, e:
|
||||
print >> sys.stderr, "---> Failed to recreate appmenus for " \
|
||||
"'{0}': {1}".format(child_vm.name, str(e))
|
||||
|
||||
main()
|
@ -1 +0,0 @@
|
||||
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
|
@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
exec /usr/libexec/qubes-appmenus/qubes-receive-appmenus $@
|
@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
VMNAME=$1
|
||||
VMTYPE=$2
|
||||
if [ -z "$VMTYPE" ]; then
|
||||
VMTYPE=appvms
|
||||
fi
|
||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
||||
APPSDIR=$VMDIR/apps
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: $0 <vmname> [appvms|vm-templates|servicevms]"
|
||||
exit
|
||||
fi
|
||||
|
||||
if ls $APPSDIR/*.directory $APPSDIR/*.desktop > /dev/null 2>&1; then
|
||||
LC_COLLATE=C xdg-desktop-menu uninstall $APPSDIR/*.directory $APPSDIR/*.desktop
|
||||
rm -f $APPSDIR/*.desktop $APPSDIR/*.directory
|
||||
rm -f $HOME/.config/menus/applications-merged/user-$VMNAME-vm.menu
|
||||
fi
|
||||
|
||||
if [ -n "$KDE_SESSION_UID" ]; then
|
||||
kbuildsycoca4
|
||||
fi
|
@ -21,7 +21,7 @@ install: manpages
|
||||
manpages: $(TOOLS_DOCS)
|
||||
|
||||
preview: $(rst)
|
||||
pandoc -s -f rst -t man $(rst) | groff -mandoc -Tlatin1 | less -R
|
||||
$(PANDOC) $(rst) | groff -mandoc -Tlatin1 | less -R
|
||||
|
||||
clean:
|
||||
rm -f $(TOOLS_DOCS)
|
||||
|
@ -6,14 +6,14 @@ NAME
|
||||
====
|
||||
qubes-dom0-update - update software in dom0
|
||||
|
||||
:Date: 2012-04-13
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
| qubes-dom0-update [--clean] [--check-only] [--gui] [<yum opts>] [<pkg list>]
|
||||
| qubes-dom0-update [--action=ACTION] [--clean] [--check-only] [--gui] [<yum opts>] [<pkg list>]
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
--action=ACTION
|
||||
Allows to pass a yum command, such as "install", "search", "downgrade" etc.
|
||||
--clean
|
||||
Clean yum cache before doing anything
|
||||
--check-only
|
||||
|
@ -6,8 +6,6 @@ NAME
|
||||
====
|
||||
qvm-sync-appmenus - updates desktop file templates for given StandaloneVM or TemplateVM
|
||||
|
||||
:Date: 2012-04-11
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
| qvm-sync-appmenus [options] <vm-name>
|
||||
|
85
dom0-updates/patch-dnf-yum-config
Executable file
@ -0,0 +1,85 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2015 Marek Marczykowski-Górecki
|
||||
# <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
BEGIN_MARKER="### QUBES BEGIN ###"
|
||||
END_MARKER="### QUBES END ###"
|
||||
|
||||
set -e
|
||||
|
||||
### helper functions begin ###
|
||||
|
||||
# set proxy in given config file
|
||||
update_conf() {
|
||||
local CONF_PATH="$1"
|
||||
local CONF_OPTIONS="$2"
|
||||
|
||||
# Ensure that Qubes conf markers are present in the file
|
||||
if ! grep -q "$BEGIN_MARKER" $CONF_PATH; then
|
||||
if grep -q "$END_MARKER" $CONF_PATH; then
|
||||
echo "ERROR: found QUBES END marker but not QUBES BEGIN in ${CONF_PATH}" >&2
|
||||
echo "Fix the file by either removing both of them, or adding missing back and retry" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp $CONF_PATH ${CONF_PATH}.qubes-orig
|
||||
echo "$BEGIN_MARKER" >> $CONF_PATH
|
||||
echo "$END_MARKER" >> $CONF_PATH
|
||||
elif ! grep -q "$END_MARKER" $CONF_PATH; then
|
||||
echo "ERROR: found QUBES BEGIN marker but not QUBES END in ${CONF_PATH}" >&2
|
||||
echo "Fix the file by either removing both of them, or adding missing back and retry" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare config block
|
||||
local tmpfile=`mktemp`
|
||||
cat > ${tmpfile} <<EOF
|
||||
# This part of configuration, until QUBES END, is automatically generated by
|
||||
# $0. All changes here will be overriden.
|
||||
# If you want to override any option set here, set it again to desired value,
|
||||
# below this section
|
||||
$CONF_OPTIONS
|
||||
EOF
|
||||
|
||||
# And insert it between the markers
|
||||
sed -i -e "/^$BEGIN_MARKER$/,/^$END_MARKER$/{
|
||||
/^$END_MARKER$/b
|
||||
/^$BEGIN_MARKER$/!d
|
||||
r ${tmpfile}
|
||||
}" ${CONF_PATH}
|
||||
rm -f ${tmpfile}
|
||||
}
|
||||
|
||||
### helper functions end
|
||||
|
||||
if [ -e /etc/dnf/dnf.conf ]; then
|
||||
update_conf /etc/dnf/dnf.conf "
|
||||
reposdir=/etc/yum.real.repos.d
|
||||
installonlypkgs = kernel, kernel-qubes-vm"
|
||||
fi
|
||||
|
||||
if [ -e /etc/yum.conf ]; then
|
||||
update_conf /etc/yum.conf "
|
||||
reposdir=/etc/yum.real.repos.d
|
||||
installonlypkgs = kernel, kernel-qubes-vm
|
||||
distroverpkg = qubes-release"
|
||||
fi
|
||||
|
@ -1,6 +1,29 @@
|
||||
#!/bin/bash
|
||||
|
||||
UPDATEVM=`qubes-prefs --get updatevm`
|
||||
escape_args() {
|
||||
local eargs=""
|
||||
|
||||
for arg in "$@"; do
|
||||
printf -v eargs '%s%q ' "$eargs" "$arg"
|
||||
done
|
||||
|
||||
echo "${eargs%?}"
|
||||
}
|
||||
|
||||
find_regex_in_args() {
|
||||
local regex="${1}"
|
||||
shift 1
|
||||
|
||||
for arg in "${@}"; do
|
||||
if echo "${arg}" | grep -q -e "${regex}"; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
UPDATEVM=`qubes-prefs --force-root updatevm`
|
||||
UPDATES_STAT_FILE=/var/lib/qubes/updates/dom0-updates-available
|
||||
|
||||
if [ -z "$UPDATEVM" ]; then
|
||||
@ -13,22 +36,26 @@ if [ "$1" = "--help" ]; then
|
||||
echo "it checks for updates for installed packages"
|
||||
echo ""
|
||||
echo "Usage: $0 [--clean] [--check-only] [--gui] [<pkg list>]"
|
||||
echo " --clean clean yum cache before doing anything"
|
||||
echo " --clean clean dnf cache before doing anything"
|
||||
echo " --check-only only check for updates (no install)"
|
||||
echo " --gui use gpk-update-viewer for update selection"
|
||||
echo " --action=... use specific dnf action, instead of automatic install/update"
|
||||
echo " <pkg list> download (and install if run by root) new packages"
|
||||
echo " in dom0 instead of updating"
|
||||
exit
|
||||
fi
|
||||
|
||||
PKGS=
|
||||
YUM_OPTS=
|
||||
PKGS=()
|
||||
YUM_OPTS=()
|
||||
GUI=
|
||||
CHECK_ONLY=
|
||||
ALL_OPTS=$*
|
||||
ALL_OPTS=( "${@}" )
|
||||
YUM_ACTION=
|
||||
QVMRUN_OPTS=
|
||||
CLEAN=
|
||||
# Filter out some yum options and collect packages list
|
||||
TEMPLATE=
|
||||
TEMPLATE_BACKUP=
|
||||
# Filter out some dnf options and collect packages list
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--enablerepo=*|\
|
||||
@ -43,32 +70,82 @@ while [ $# -gt 0 ]; do
|
||||
--check-only)
|
||||
CHECK_ONLY=1
|
||||
;;
|
||||
--action=*)
|
||||
YUM_ACTION=${1#--action=}
|
||||
;;
|
||||
-*)
|
||||
YUM_OPTS="$YUM_OPTS $1"
|
||||
YUM_OPTS+=( "${1}" )
|
||||
;;
|
||||
*)
|
||||
PKGS="$PKGS $1"
|
||||
PKGS+=( "${1}" )
|
||||
if [ -z "$YUM_ACTION" ]; then
|
||||
YUM_ACTION=install
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Prevent implicit update of template - this would override user changes -
|
||||
# but do allow explicit template upgrade, downgrade, reinstall
|
||||
if [ "$YUM_ACTION" == "reinstall" ] || [ "$YUM_ACTION" == "upgrade" ] || [ "$YUM_ACTION" == "upgrade-to" ] \
|
||||
|| [ "$YUM_ACTION" == "downgrade" ] && find_regex_in_args '^qubes-template-' "${PKGS[@]}"; then
|
||||
TEMPLATE_EXCLUDE_OPTS=()
|
||||
echo "WARNING: Replacing a template will erase all files in template's /home and /rw !"
|
||||
|
||||
# At least one package name matches the regex '^qubes-template-',
|
||||
# so if there is only one package name in the array, then the
|
||||
# code can safely assume that the array includes only a template
|
||||
# package name.
|
||||
if [[ ${#PKGS[@]} -eq 1 ]]; then
|
||||
ONEPKG="$(echo "${PKGS[0]}" | sed -r 's/-[0-9]+(\.[0-9-]+)+(\.noarch)*$//')" # Remove version suffix
|
||||
TEMPLATE=${ONEPKG#qubes-template-} # Remove prefix
|
||||
|
||||
if qvm-shutdown --wait $TEMPLATE ; then
|
||||
echo "Template VM halted"
|
||||
fi
|
||||
|
||||
# Try to avoid unrecoverable failures when operating on the template of
|
||||
# the UpdateVM by making a backup first.
|
||||
UPDATEVM_TEMPLATE=$(qvm-prefs -- "$UPDATEVM" template 2>/dev/null)
|
||||
if [ X"$UPDATEVM_TEMPLATE" = X"$TEMPLATE" ]; then
|
||||
TEMPLATE_BACKUP="${TEMPLATE}-backup-$(date +%Y%m%d)-$(mktemp -u XXXX)"
|
||||
TEMPLATE_BACKUP=${TEMPLATE_BACKUP:0:31}
|
||||
echo "Attempting to operate on template of UpdateVM... backing up $TEMPLATE to $TEMPLATE_BACKUP"
|
||||
if ! qvm-clone -- "$TEMPLATE" "$TEMPLATE_BACKUP"; then
|
||||
echo "ERROR: Unable to make backup of UpdateVM template!" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "ERROR: Specify only one package to reinstall template"
|
||||
exit 1
|
||||
fi
|
||||
elif [ "$YUM_ACTION" == "search" ] || [ "$YUM_ACTION" == "info" ]; then # No need to shutdown for search/info
|
||||
TEMPLATE_EXCLUDE_OPTS=()
|
||||
else
|
||||
TEMPLATE_EXCLUDE_OPTS=( "--exclude=$(rpm -qa --qf '%{NAME},' qubes-template-\*|head -c -1)" )
|
||||
fi
|
||||
|
||||
YUM_OPTS=( "${TEMPLATE_EXCLUDE_OPTS[@]}" "${YUM_OPTS[@]}" )
|
||||
ALL_OPTS=( "${TEMPLATE_EXCLUDE_OPTS[@]}" "${ALL_OPTS[@]}" )
|
||||
|
||||
ID=$(id -ur)
|
||||
if [ $ID != 0 -a -z "$GUI" -a -z "$CHECK_ONLY" ] ; then
|
||||
echo "This script should be run as root (when used in console mode), use sudo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$GUI" == "1" -a -n "$PKGS" ]; then
|
||||
if [ "$GUI" == "1" -a ${#PKGS[@]} -ne 0 ]; then
|
||||
echo "ERROR: GUI mode can be used only for updates" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$GUI" == "1" ]; then
|
||||
apps="yumex apper gpk-update-viewer"
|
||||
apps="xterm konsole yumex apper gpk-update-viewer"
|
||||
|
||||
if [ -n "$KDE_FULL_SESSION" ]; then
|
||||
apps="apper yumex gpk-update-viewer"
|
||||
apps="konsole xterm apper yumex gpk-update-viewer"
|
||||
fi
|
||||
|
||||
guiapp=
|
||||
@ -76,7 +153,9 @@ if [ "$GUI" == "1" ]; then
|
||||
if type $app &>/dev/null; then
|
||||
guiapp=$app
|
||||
case $guiapp in
|
||||
apper) guiapp="apper --updates" ;;
|
||||
apper) guiapp="apper --updates --nofork" ;;
|
||||
xterm) guiapp="xterm -e sudo dnf update" ;;
|
||||
konsole) guiapp="konsole --hold -e sudo dnf update" ;;
|
||||
*) guiapp=$app ;;
|
||||
esac
|
||||
break;
|
||||
@ -84,7 +163,7 @@ if [ "$GUI" == "1" ]; then
|
||||
done
|
||||
|
||||
if [ -z "$guiapp" ]; then
|
||||
message1="You don't have installed any supported yum frontend."
|
||||
message1="You don't have any supported dnf frontend installed."
|
||||
message2="Install (using qubes-dom0-update) one of: $apps"
|
||||
|
||||
if [ "$KDE_FULL_SESSION" ]; then
|
||||
@ -101,28 +180,28 @@ if [ "$GUI" != "1" ]; then
|
||||
QVMRUN_OPTS=--nogui
|
||||
fi
|
||||
|
||||
# Do not start VM automaticaly when running from cron (only checking for updates)
|
||||
if [ "$CHECK_ONLY" == "1" ] && ! xl domid $UPDATEVM > /dev/null 2>&1; then
|
||||
# Do not start VM automatically when running from cron (only checking for updates)
|
||||
if [ "$CHECK_ONLY" == "1" ] && ! qvm-check -q --running $UPDATEVM > /dev/null 2>&1; then
|
||||
echo "ERROR: UpdateVM not running, not starting it in non-interactive mode" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$CLEAN" ]; then
|
||||
rm -f /var/lib/qubes/updates/rpm/*
|
||||
rm -f /var/lib/qubes/updates/repodata/*
|
||||
fi
|
||||
|
||||
# We should ensure the clocks in Dom0 and UpdateVM are in sync
|
||||
# becuase otherwise yum might complain about future timestamps
|
||||
qvm-sync-clock
|
||||
rm -f /var/lib/qubes/updates/errors
|
||||
|
||||
echo "Using $UPDATEVM as UpdateVM to download updates for Dom0; this may take some time..." >&2
|
||||
|
||||
# Start VM if not running already
|
||||
qvm-run $QVMRUN_OPTS -a $UPDATEVM true || exit 1
|
||||
# qvm-run by default auto-starts the VM if not running
|
||||
qvm-run --nogui -q -u root $UPDATEVM 'mkdir -m 775 -p /var/lib/qubes/dom0-updates/' || exit 1
|
||||
qvm-run --nogui -q -u root $UPDATEVM 'chown user:user /var/lib/qubes/dom0-updates/' || exit 1
|
||||
qvm-run --nogui -q $UPDATEVM 'rm -rf /var/lib/qubes/dom0-updates/etc' || exit 1
|
||||
tar c /var/lib/rpm /etc/yum.repos.d /etc/yum.conf /etc/dnf/dnf.conf 2>/dev/null | \
|
||||
qvm-run --nogui -q --pass-io "$UPDATEVM" 'LC_MESSAGES=C tar x -C /var/lib/qubes/dom0-updates 2>&1 | grep -v -E "s in the future"'
|
||||
|
||||
tar c /var/lib/rpm /etc/yum.repos.d /etc/yum.conf 2>/dev/null | qvm-run -p "$UPDATEVM" 'tar x -C /var/lib/qubes/dom0-updates'
|
||||
|
||||
qvm-run $QVMRUN_OPTS --pass-io $UPDATEVM "/usr/lib/qubes/qubes-download-dom0-updates.sh --doit --nogui $ALL_OPTS"
|
||||
qvm-run $QVMRUN_OPTS --pass-io $UPDATEVM "script --quiet --return --command '/usr/lib/qubes/qubes-download-dom0-updates.sh --doit --nogui $(escape_args "${ALL_OPTS[@]}")' /dev/null" < /dev/null
|
||||
RETCODE=$?
|
||||
if [ "$CHECK_ONLY" == "1" ]; then
|
||||
exit $RETCODE
|
||||
@ -135,23 +214,51 @@ while pidof -x qubes-receive-updates >/dev/null; do sleep 0.5; done
|
||||
if [ -r /var/lib/qubes/updates/errors ]; then
|
||||
echo "*** ERROR while receiving updates:" >&2
|
||||
cat /var/lib/qubes/updates/errors >&2
|
||||
echo "--> if you want to use packages that were downloaded correctly, use yum directly now" >&2
|
||||
echo "--> if you want to use packages that were downloaded correctly, use dnf directly now" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "x$PKGS" != "x" ]; then
|
||||
yum $YUM_OPTS install $PKGS
|
||||
if [ -z "$YUM_ACTION" ]; then
|
||||
YUM_ACTION=upgrade
|
||||
fi
|
||||
|
||||
if [ ${#PKGS[@]} -gt 0 ]; then
|
||||
if [ -n "$TEMPLATE" ]; then
|
||||
TEMPLATE_NETVM=$(qvm-prefs --force-root $TEMPLATE netvm)
|
||||
fi
|
||||
|
||||
dnf "${YUM_OPTS[@]}" $YUM_ACTION "${PKGS[@]}" ; RETCODE=$?
|
||||
|
||||
if [ -n "$TEMPLATE_BACKUP" -a "$RETCODE" -eq 0 ]; then
|
||||
# Remove backup, if we made one. Better to do this only on success and
|
||||
# potentially leave extra backups around than do it on an exit trap and
|
||||
# clean up more reliably but potentially brick a system.
|
||||
qvm-remove -f -- "$TEMPLATE_BACKUP"
|
||||
fi
|
||||
|
||||
if [ -n "$TEMPLATE" -a -n "$TEMPLATE_NETVM" -a x"$TEMPLATE_NETVM" != xNone ]; then
|
||||
if ! qvm-prefs --force-root -s $TEMPLATE netvm $TEMPLATE_NETVM; then
|
||||
echo "ERROR: NetVM setting could not be restored!" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
elif [ -f /var/lib/qubes/updates/repodata/repomd.xml ]; then
|
||||
# Above file exists only when at least one package was downloaded
|
||||
if [ "$GUI" == "1" ]; then
|
||||
# refresh packagekit metadata, GUI utilities use it
|
||||
pkcon refresh force
|
||||
$guiapp
|
||||
else
|
||||
yum check-update
|
||||
if [ $? -eq 100 ]; then
|
||||
yum $YUM_OPTS update
|
||||
dnf check-update
|
||||
if [ $? -eq 100 ]; then # Run dnf with options
|
||||
dnf "${YUM_OPTS[@]}" $YUM_ACTION
|
||||
fi
|
||||
fi
|
||||
yum -q check-update && rm -f $UPDATES_STAT_FILE
|
||||
dnf -q check-update && qvm-features dom0 updates-available ''
|
||||
else
|
||||
echo "No updates avaliable" >&2
|
||||
qvm-features dom0 updates-available ''
|
||||
echo "No updates available" >&2
|
||||
if [ "$GUI" == "1" ]; then
|
||||
zenity --info --title='Dom0 updates' --text='No updates available'
|
||||
fi
|
||||
fi
|
||||
|
@ -1,12 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get normal user name
|
||||
LOCAL_USER=`users | sed -e 's/root *//' | cut -d' ' -f 1`
|
||||
NOTIFY_ICON=/usr/share/qubes/icons/dom0-update-avail.svg
|
||||
UPDATES_STAT_FILE=/var/lib/qubes/updates/dom0-updates-available
|
||||
UPDATES_DISABLE_FLAG=/var/lib/qubes/updates/disable-updates
|
||||
|
||||
if [ -f "$UPDATES_DISABLE_FLAG" ]; then
|
||||
if [ "$(qvm-features dom0 service.qubes-update-check || echo 1)" != 1 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@ -19,10 +13,4 @@ if [ "$RETCODE" -ne 100 ]; then
|
||||
exit $RETCODE
|
||||
fi
|
||||
|
||||
if [ -z "$LOCAL_USER" ]; then
|
||||
echo "ERROR: no user logged in, cannot nofity about updates" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Touch stat file for qubes-manager
|
||||
touch $UPDATES_STAT_FILE
|
||||
qvm-features dom0 updates-available 1
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -18,44 +18,49 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import shutil
|
||||
import glob
|
||||
import grp
|
||||
from qubes.qubes import QubesVmCollection
|
||||
import qubesadmin
|
||||
|
||||
updates_dir = "/var/lib/qubes/updates"
|
||||
updates_rpm_dir = updates_dir + "/rpm"
|
||||
updates_repodata_dir = updates_dir + "/repodata"
|
||||
updates_error_file = updates_dir + "/errors"
|
||||
updates_error_file_handle = None
|
||||
|
||||
comps_file = None
|
||||
if os.path.exists('/usr/share/qubes/Qubes-comps.xml'):
|
||||
comps_file = '/usr/share/qubes/Qubes-comps.xml'
|
||||
|
||||
package_regex = re.compile(r"^[A-Za-z0-9._+-]{1,128}.rpm$")
|
||||
gpg_ok_regex = re.compile(r"pgp md5 OK$")
|
||||
package_regex = re.compile(r"^[A-Za-z0-9._+-]{1,128}\.rpm$")
|
||||
# example valid outputs:
|
||||
# .....rpm: rsa sha1 (md5) pgp md5 OK
|
||||
# .....rpm: (sha1) dsa sha1 md5 gpg OK
|
||||
# .....rpm: digests signatures OK
|
||||
# example INVALID outputs:
|
||||
# .....rpm: sha1 md5 OK
|
||||
# .....rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#246110c1)
|
||||
# .....rpm: digests OK
|
||||
gpg_ok_regex = re.compile(r": [a-z0-9() ]* (pgp|gpg|signatures) [a-z0-9 ]*OK$")
|
||||
|
||||
|
||||
def dom0updates_fatal(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
with open(updates_error_file, "a") as updates_error_file_handle:
|
||||
updates_error_file_handle.write(msg + "\n")
|
||||
shutil.rmtree(updates_rpm_dir)
|
||||
exit(1)
|
||||
|
||||
def dom0updates_fatal(pkg, msg):
|
||||
global updates_error_file_handle
|
||||
print >> sys.stderr, msg
|
||||
if updates_error_file_handle is None:
|
||||
updates_error_file_handle = open(updates_error_file, "a")
|
||||
updates_error_file_handle.write(msg + "\n")
|
||||
os.remove(pkg)
|
||||
|
||||
def handle_dom0updates(updatevm):
|
||||
global updates_error_file_handle
|
||||
|
||||
source=os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
source = os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
if source != updatevm.name:
|
||||
print >> sys.stderr, 'Domain ' + str(source) + ' not allowed to send dom0 updates'
|
||||
print('Domain ' + str(source) + ' not allowed to send dom0 updates',
|
||||
file=sys.stderr)
|
||||
exit(1)
|
||||
# Clean old packages
|
||||
if os.path.exists(updates_rpm_dir):
|
||||
@ -66,15 +71,18 @@ def handle_dom0updates(updatevm):
|
||||
os.remove(updates_error_file)
|
||||
os.environ['LC_ALL'] = 'C'
|
||||
qubes_gid = grp.getgrnam('qubes').gr_gid
|
||||
old_umask = os.umask(0o002)
|
||||
os.mkdir(updates_rpm_dir)
|
||||
os.chown(updates_rpm_dir, -1, qubes_gid)
|
||||
os.chmod(updates_rpm_dir, 0775)
|
||||
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker", str(os.getuid()), updates_rpm_dir])
|
||||
# Verify received files
|
||||
for untrusted_f in os.listdir(updates_rpm_dir):
|
||||
if not package_regex.match(untrusted_f):
|
||||
dom0updates_fatal(updates_rpm_dir + '/' + untrusted_f, 'Domain ' + source + ' sent unexpected file: ' + untrusted_f)
|
||||
else:
|
||||
os.chmod(updates_rpm_dir, 0o0775)
|
||||
try:
|
||||
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker",
|
||||
str(os.getuid()), updates_rpm_dir])
|
||||
# Verify received files
|
||||
for untrusted_f in os.listdir(updates_rpm_dir):
|
||||
if not package_regex.match(untrusted_f):
|
||||
raise Exception(
|
||||
'Domain ' + source + ' sent unexpected file')
|
||||
f = untrusted_f
|
||||
assert '/' not in f
|
||||
assert '\0' not in f
|
||||
@ -82,45 +90,42 @@ def handle_dom0updates(updatevm):
|
||||
|
||||
full_path = updates_rpm_dir + "/" + f
|
||||
if os.path.islink(full_path) or not os.path.isfile(full_path):
|
||||
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not regular file')
|
||||
p = subprocess.Popen (["/bin/rpm", "-K", full_path],
|
||||
raise Exception(
|
||||
'Domain ' + source + ' sent not regular file')
|
||||
p = subprocess.Popen(["/bin/rpm", "-K", full_path],
|
||||
stdout=subprocess.PIPE)
|
||||
output = p.communicate()[0]
|
||||
output = p.communicate()[0].decode('ascii')
|
||||
if p.returncode != 0:
|
||||
dom0updates_fatal(full_path, 'Error while verifing %s signature: %s' % (f, output))
|
||||
raise Exception(
|
||||
'Error while verifing %s signature: %s' % (f, output))
|
||||
if not gpg_ok_regex.search(output.strip()):
|
||||
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not signed rpm: ' + f)
|
||||
if updates_error_file_handle is not None:
|
||||
updates_error_file_handle.close()
|
||||
raise Exception(
|
||||
'Domain ' + source + ' sent not signed rpm: ' + f)
|
||||
except Exception as e:
|
||||
dom0updates_fatal(str(e))
|
||||
# After updates received - create repo metadata
|
||||
createrepo_cmd = ["/usr/bin/createrepo"]
|
||||
createrepo_cmd = ["/usr/bin/createrepo_c"]
|
||||
if comps_file:
|
||||
createrepo_cmd += ["-g", comps_file]
|
||||
createrepo_cmd += ["-q", updates_dir]
|
||||
old_umask = os.umask(002)
|
||||
subprocess.check_call(createrepo_cmd)
|
||||
os.chown(updates_repodata_dir, -1, qubes_gid)
|
||||
os.chmod(updates_repodata_dir, 0775)
|
||||
os.chmod(updates_repodata_dir, 0o0775)
|
||||
# Clean old cache
|
||||
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"], stdout=sys.stderr)
|
||||
# This will fail because of "smart" detection of no-network, but it will invalidate the cache
|
||||
try:
|
||||
null = open('/dev/null','w')
|
||||
subprocess.call(["/usr/bin/pkcon", "refresh"], stdout=null)
|
||||
null.close()
|
||||
except:
|
||||
pass
|
||||
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"],
|
||||
stdout=sys.stderr)
|
||||
os.umask(old_umask)
|
||||
exit(0)
|
||||
|
||||
def main():
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
def main():
|
||||
app = qubesadmin.Qubes()
|
||||
|
||||
updatevm = qvm_collection.get_updatevm_vm()
|
||||
updatevm = app.updatevm
|
||||
if updatevm is None:
|
||||
exit(1)
|
||||
handle_dom0updates(updatevm)
|
||||
|
||||
main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1,4 +0,0 @@
|
||||
# Apprently some of the drivers required when using a processor with AESNI for LUKS
|
||||
# are missing in the initramfs, so lets include them manually here:
|
||||
|
||||
add_drivers+=" xts aesni-intel aes-x86_64 crc32c-intel ghash-clmulni-intel salsa20-x86_64 twofish-x86_64 "
|
6
dracut/dracut.conf.d/qubes-disable-network.conf
Normal file
@ -0,0 +1,6 @@
|
||||
# Omission of network and kernel-network-modules is needed
|
||||
# to avoid letting the initramfs load kernel modules related
|
||||
# to networking, even if PCI devices are seized by Xen's
|
||||
# pciback kernel module.
|
||||
|
||||
omit_dracutmodules+=" network kernel-network-modules "
|
@ -3,5 +3,9 @@
|
||||
|
||||
installkernel() {
|
||||
# ehci-hcd split off
|
||||
instmods ehci-pci ehci-platform || :
|
||||
hostonly='' instmods ehci-pci ehci-platform || :
|
||||
# xhci-hcd split off
|
||||
hostonly='' instmods xhci-pci xhci-plat-hcd || :
|
||||
# ohci-hcd split off
|
||||
hostonly='' instmods ohci-pci || :
|
||||
}
|
||||
|
16
dracut/modules.d/90macbook12-spi-driver/module-setup.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/usr/bin/bash
|
||||
# Add roadrunner2/macbook12-spi-driver drivers to initramfs for supporting keyboard, touchpad, touchbar in the MacBooks.
|
||||
# Pre-requisite: these drivers need to be included in the Linux kernel package.
|
||||
|
||||
check() {
|
||||
grep -q ^MacBook /sys/devices/virtual/dmi/id/product_name || return 255
|
||||
}
|
||||
|
||||
installkernel() {
|
||||
hostonly='' instmods intel_lpss intel_lpss_pci spi_pxa2xx_platform spi_pxa2xx_pci applespi apple_ib_tb
|
||||
}
|
||||
|
||||
install() {
|
||||
echo "options apple_ib_tb fnmode=2" >> "${initdir}/etc/modprobe.d/macbook12-spi-driver.conf"
|
||||
echo "options applespi fnremap=1" >> "${initdir}/etc/modprobe.d/macbook12-spi-driver.conf"
|
||||
}
|
@ -1,3 +1,7 @@
|
||||
#!/bin/bash
|
||||
modinfo -k $kernel pciback > /dev/null 2>&1 && instmods pciback
|
||||
modinfo -k $kernel xen-pciback > /dev/null 2>&1 && instmods xen-pciback
|
||||
|
||||
for mod in pciback xen-pciback; do
|
||||
if modinfo -k "${kernel}" "${mod}" >/dev/null 2>&1; then
|
||||
instmods "${mod}"
|
||||
fi
|
||||
done
|
||||
|
@ -8,6 +8,11 @@ install() {
|
||||
}
|
||||
|
||||
installkernel() {
|
||||
modinfo -k $kernel pciback > /dev/null 2>&1 && hostonly='' instmods pciback
|
||||
modinfo -k $kernel xen-pciback > /dev/null 2>&1 && hostonly='' instmods xen-pciback
|
||||
local mod=
|
||||
|
||||
for mod in pciback xen-pciback; do
|
||||
if modinfo -k "${kernel}" "${mod}" >/dev/null 2>&1; then
|
||||
hostonly='' instmods "${mod}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
@ -3,14 +3,23 @@
|
||||
type getarg >/dev/null 2>&1 || . /lib/dracut-lib.sh
|
||||
|
||||
# Find all networking devices currenly installed...
|
||||
HIDE_PCI=`lspci -mm -n | grep '^[^ ]* "02'|awk '{ ORS="";print "(" $1 ")";}'`
|
||||
HIDE_PCI="`lspci -mm -n | grep '^[^ ]* "02'|awk '{print $1}'`"
|
||||
|
||||
# ... and optionally all USB controllers...
|
||||
if getargbool 0 rd.qubes.hide_all_usb; then
|
||||
HIDE_PCI=$HIDE_PCI`lspci -mm -n | grep '^[^ ]* "0c03'|awk '{ ORS="";print "(" $1 ")";}'`
|
||||
HIDE_PCI="$HIDE_PCI `lspci -mm -n | grep '^[^ ]* "0c03'|awk '{print $1}'`"
|
||||
fi
|
||||
|
||||
HIDE_PCI=$HIDE_PCI`getarg rd.qubes.hide_pci | tr ',' '\n'|awk '{ ORS="";print "(" $1 ")";}'`
|
||||
HIDE_PCI="$HIDE_PCI `getarg rd.qubes.hide_pci | tr ',' ' '`"
|
||||
|
||||
modprobe xen-pciback 2>/dev/null || :
|
||||
|
||||
# ... and hide them so that Dom0 doesn't load drivers for them
|
||||
modprobe pciback hide=$HIDE_PCI 2> /dev/null || modprobe xen-pciback hide=$HIDE_PCI
|
||||
|
||||
for dev in $HIDE_PCI; do
|
||||
BDF=0000:$dev
|
||||
if [ -e /sys/bus/pci/devices/$BDF/driver ]; then
|
||||
echo -n $BDF > /sys/bus/pci/devices/$BDF/driver/unbind
|
||||
fi
|
||||
echo -n $BDF > /sys/bus/pci/drivers/pciback/new_slot
|
||||
echo -n $BDF > /sys/bus/pci/drivers/pciback/bind
|
||||
done
|
||||
|
8
file-copy-vm/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
CFLAGS=-g -O2 -Wall -Wextra -Werror -I. -fPIC -pie
|
||||
all: qfile-dom0-agent
|
||||
qfile-dom0-agent: qfile-dom0-agent.o
|
||||
$(CC) -pie -g -o $@ $^ -lqubes-rpc-filecopy
|
||||
|
||||
clean:
|
||||
rm -f qfile-dom0-agent *.o
|
||||
|
94
file-copy-vm/qfile-dom0-agent.c
Normal file
@ -0,0 +1,94 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <libqubes-rpc-filecopy.h>
|
||||
|
||||
void display_error(const char *fmt, va_list args) {
|
||||
char *dialog_cmd;
|
||||
char buf[1024];
|
||||
struct stat st_buf;
|
||||
int ret;
|
||||
|
||||
(void) vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
ret = stat("/usr/bin/kdialog", &st_buf);
|
||||
#define KDIALOG_CMD "kdialog --title 'File copy/move error' --sorry "
|
||||
#define ZENITY_CMD "zenity --title 'File copy/move error' --warning --text "
|
||||
if (asprintf(&dialog_cmd, "%s '%s: %s (error type: %s)'",
|
||||
ret==0 ? KDIALOG_CMD : ZENITY_CMD,
|
||||
program_invocation_short_name, buf, strerror(errno)) < 0) {
|
||||
fprintf(stderr, "Failed to allocate memory for error message :(\n");
|
||||
return;
|
||||
}
|
||||
#undef KDIALOG_CMD
|
||||
#undef ZENITY_CMD
|
||||
fprintf(stderr, "%s\n", buf);
|
||||
system(dialog_cmd);
|
||||
}
|
||||
|
||||
_Noreturn void gui_fatal(const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
display_error(fmt, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *get_abs_path(const char *cwd, const char *pathname)
|
||||
{
|
||||
char *ret;
|
||||
if (pathname[0] == '/')
|
||||
return strdup(pathname);
|
||||
if (asprintf(&ret, "%s/%s", cwd, pathname) < 0)
|
||||
return NULL;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
char *entry;
|
||||
char *cwd;
|
||||
char *sep;
|
||||
int ignore_symlinks = 0;
|
||||
|
||||
qfile_pack_init();
|
||||
register_error_handler(display_error);
|
||||
cwd = getcwd(NULL, 0);
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--ignore-symlinks")==0) {
|
||||
ignore_symlinks = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
entry = get_abs_path(cwd, argv[i]);
|
||||
|
||||
do {
|
||||
sep = rindex(entry, '/');
|
||||
if (!sep)
|
||||
gui_fatal
|
||||
("Internal error: nonabsolute filenames not allowed");
|
||||
*sep = 0;
|
||||
} while (sep[1] == 0);
|
||||
if (entry[0] == 0) {
|
||||
if (chdir("/") < 0) {
|
||||
gui_fatal("Internal error: chdir(\"/\") failed?!");
|
||||
}
|
||||
} else if (chdir(entry))
|
||||
gui_fatal("chdir to %s", entry);
|
||||
do_fs_walk(sep + 1, ignore_symlinks);
|
||||
free(entry);
|
||||
}
|
||||
notify_end_and_wait_for_result();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
26
file-copy-vm/qvm-copy
Normal file
@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
set -e -o pipefail
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2015 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
echo "${0##*/} is not supported in dom0; use ${0##*/}-to-vm instead."
|
||||
|
||||
exit 1
|
43
file-copy-vm/qvm-copy-to-vm
Normal file
@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
set -e -o pipefail
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2015 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
if [ $# -lt 2 ] ; then
|
||||
echo usage: $0 'dest_vmname file [file]+'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VM="$1"
|
||||
shift
|
||||
|
||||
TMPDIR=`mktemp -d`
|
||||
trap 'rm -rf -- "$TMPDIR"' EXIT
|
||||
RESPONSE=$TMPDIR/response
|
||||
mkfifo -- "$RESPONSE"
|
||||
|
||||
# can't use $@ with --localcmd, and $* would fail on whitespace
|
||||
/usr/lib/qubes/qfile-dom0-agent "$@" <"$RESPONSE" |
|
||||
qvm-run --pass-io --service -- "$VM" "qubes.Filecopy" >"$RESPONSE"
|
||||
|
||||
if [ "${0##*/}" = "qvm-move-to-vm" ]; then
|
||||
rm -rf -- "$@"
|
||||
fi
|
BIN
icons/black.png
Before Width: | Height: | Size: 169 KiB |
BIN
icons/blue.png
Before Width: | Height: | Size: 181 KiB |
@ -1,10 +0,0 @@
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
This copyright and license notice covers the images in this directory.
|
||||
************************************************************************
|
||||
|
||||
TITLE: Crystal Project Icons
|
||||
AUTHOR: Everaldo Coelho
|
||||
SITE: http://www.everaldo.com
|
||||
CONTACT: everaldo@everaldo.com
|
||||
|
||||
Copyright (c) 2006-2007 Everaldo Coelho.
|
@ -1 +0,0 @@
|
||||
dom0-update-avail icon from gnome-packagekit project distributed under GPLv2
|
@ -1 +0,0 @@
|
||||
Color padlock images downloaded from www.openclipart.org
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.5 KiB |
BIN
icons/gray.png
Before Width: | Height: | Size: 192 KiB |
BIN
icons/green.png
Before Width: | Height: | Size: 187 KiB |
BIN
icons/netvm.png
Before Width: | Height: | Size: 15 KiB |
BIN
icons/orange.png
Before Width: | Height: | Size: 188 KiB |
BIN
icons/purple.png
Before Width: | Height: | Size: 188 KiB |
BIN
icons/qubes.png
Before Width: | Height: | Size: 20 KiB |
BIN
icons/red.png
Before Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
icons/yellow.png
Before Width: | Height: | Size: 185 KiB |
@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
sync_qubes_vms_wallclock()
|
||||
{
|
||||
# Sync all VMs based on dom0 clock
|
||||
DATE=$(date)
|
||||
echo
|
||||
echo "Syncing VMs clock to: $DATE"
|
||||
qvm-run --all -u root "date -s \"$DATE\""
|
||||
# Then try to sync from the network
|
||||
/usr/bin/qvm-sync-clock &
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
thaw|resume) sync_qubes_vms_wallclock ;;
|
||||
# Kill qvm-sync-clock (if running) to not desync time after resume
|
||||
suspend|hibernate)
|
||||
killall qvm-sync-clock 2> /dev/null
|
||||
exit 0
|
||||
;;
|
||||
*) exit 0 ;;
|
||||
esac
|
@ -1,37 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
get_running_netvms() {
|
||||
# Actually get running VMs with PCI devices attached
|
||||
RUNNING_VMS=`xl list | tail -n +3 | cut -f 1 -d " "`
|
||||
RUNNING_NETVMS=""
|
||||
for VM in $RUNNING_VMS; do
|
||||
if [ -n "`xl pci-list $VM|tail -n +2`" ]; then
|
||||
echo "$VM"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
suspend_net()
|
||||
{
|
||||
for VM in `get_running_netvms`; do
|
||||
qvm-run -u root --pass-io $VM 'QUBESRPC qubes.SuspendPre dom0'
|
||||
done
|
||||
# Ignore exit status from netvm...
|
||||
return 0
|
||||
}
|
||||
|
||||
resume_net()
|
||||
{
|
||||
for VM in `get_running_netvms`; do
|
||||
qvm-run -u root --pass-io $VM 'QUBESRPC qubes.SuspendPost dom0'
|
||||
done
|
||||
# Ignore exit status from netvm...
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
resume) resume_net ;;
|
||||
suspend) suspend_net ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
@ -1,25 +1,12 @@
|
||||
#!/usr/bin/python
|
||||
#!/bin/sh
|
||||
|
||||
from qubes.qubes import QubesVmCollection,QubesException
|
||||
import sys
|
||||
|
||||
qc = QubesVmCollection()
|
||||
qc.lock_db_for_reading()
|
||||
qc.load()
|
||||
qc.unlock_db()
|
||||
|
||||
if sys.argv[1] in ["suspend", "hibernate"]:
|
||||
for vm in qc.values():
|
||||
if vm.is_running():
|
||||
try:
|
||||
vm.suspend()
|
||||
except Exception as e:
|
||||
print >>sys.stderr, "Failed to suspend VM %s: %s" % (vm.name, e.message)
|
||||
|
||||
elif sys.argv[1] in ["resume", "thaw"]:
|
||||
for vm in qc.values():
|
||||
if vm.get_power_state() in ["Paused", "Suspended"]:
|
||||
try:
|
||||
vm.resume()
|
||||
except Exception as e:
|
||||
print >>sys.stderr, "Failed to resume VM %s: %s" % (vm.name, e.message)
|
||||
case "$1" in
|
||||
suspend|hibernate)
|
||||
qubesd-query -e --fail -c /var/run/qubesd.internal.sock \
|
||||
dom0 internal.SuspendPre dom0 | tr '\0' ' '
|
||||
;;
|
||||
resume|thaw)
|
||||
qubesd-query -e --fail -c /var/run/qubesd.internal.sock \
|
||||
dom0 internal.SuspendPost dom0 | tr '\0' ' '
|
||||
;;
|
||||
esac
|
||||
|
@ -7,12 +7,8 @@ StopWhenUnneeded=yes
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
StandardOutput=syslog
|
||||
ExecStartPre=/usr/lib64/pm-utils/sleep.d/01qubes-sync-vms-clock suspend suspend
|
||||
ExecStartPre=/usr/lib64/pm-utils/sleep.d/51qubes-suspend-netvm suspend suspend
|
||||
ExecStart=/usr/lib64/pm-utils/sleep.d/52qubes-pause-vms suspend suspend
|
||||
ExecStop=/usr/lib64/pm-utils/sleep.d/52qubes-pause-vms resume suspend
|
||||
ExecStopPost=/usr/lib64/pm-utils/sleep.d/51qubes-suspend-netvm resume suspend
|
||||
ExecStopPost=/usr/lib64/pm-utils/sleep.d/01qubes-sync-vms-clock resume suspend
|
||||
|
||||
[Install]
|
||||
WantedBy=sleep.target
|
||||
|
@ -1,12 +0,0 @@
|
||||
CC=gcc
|
||||
CFLAGS+=-I. -g -Wall -Wextra -Werror -pie -fPIC
|
||||
XENLIBS=-lvchan -lxenstore -lxenctrl
|
||||
LIBS=$(XENLIBS) -lqrexec-utils
|
||||
|
||||
all: qrexec-daemon qrexec-client
|
||||
qrexec-daemon: qrexec-daemon.o
|
||||
$(CC) -pie -g -o qrexec-daemon qrexec-daemon.o $(LIBS)
|
||||
qrexec-client: qrexec-client.o
|
||||
$(CC) -pie -g -o qrexec-client qrexec-client.o $(LIBS) -lpthread
|
||||
clean:
|
||||
rm -f *.o *~ qrexec-daemon qrexec-client
|
@ -1,64 +0,0 @@
|
||||
Currently (after commit 2600134e3bb781fca25fe77e464f8b875741dc83),
|
||||
qrexec_agent can request a service (specified by a "exec_index") to be
|
||||
executed on a different VM or dom0. Access control is enforced in dom0 via
|
||||
files in /etc/qubes_rpc/policy. File copy, Open in Dispvm, sync appmenus,
|
||||
upload updates to dom0 - they all have been ported to the new API.
|
||||
See the quick HOWTO section on how to add a new service. Note we have
|
||||
qvm-open-in-vm utility practically for free.
|
||||
|
||||
CHANGES
|
||||
|
||||
Besides flexibility offered by /etc/qubes_rpc/policy, writing a client
|
||||
is much simpler now. The workflow used to be (using "filecopy" service as
|
||||
an example):
|
||||
a) "filecopy_ui" process places job description in some spool directory,
|
||||
signals qrexec_agent to signal qrexec_daemon
|
||||
b) qrexec_daemon executes "qrexec_client -d domain filecopy_worker ...."
|
||||
and "filecopy_worker" process needed to parse spool and retrieve job
|
||||
description from there. Particularly, "filecopy_ui" had no connection to
|
||||
remote.
|
||||
Now, the flow is:
|
||||
a) qrexec_client_vm process obtains 3 unix socket descriptors from
|
||||
qrexec_agent, dup stdin/out/err to them; forms "existing_process_handle" from
|
||||
them
|
||||
b) qrexec_client_vm signals qrexec_agent to signal qrexec_daemon, with a
|
||||
"exec_index" (so, type of service) as an argument
|
||||
c) qrexec_daemon executed "qrexec_client -d domain -c existing_process_handle ...."
|
||||
d) qrexec_client_vm execve filecopy_program.
|
||||
|
||||
Thus, there is only one service program, and it has direct access to remote via
|
||||
stdin/stdout.
|
||||
|
||||
HOWTO
|
||||
|
||||
Let's add a new "test.Add" service, that will add two numbers. We need the
|
||||
following files in the template fs:
|
||||
==========================
|
||||
/usr/bin/our_test_add_client:
|
||||
#!/bin/sh
|
||||
echo $1 $2
|
||||
exec cat >&2
|
||||
# more correct: exec cat >&$SAVED_FD_1, but do not scare the reader
|
||||
==========================
|
||||
/usr/bin/our_test_add_server:
|
||||
#!/bin/sh
|
||||
read arg1 arg2
|
||||
echo $(($arg1+$arg2))
|
||||
==========================
|
||||
/etc/qubes_rpc/test.Add:
|
||||
/usr/bin/our_test_add_server
|
||||
|
||||
Now, on the client side, we start the client via
|
||||
/usr/lib/qubes/qrexec_client_vm target_vm test.Add /usr/bin/our_test_add_client 11 22
|
||||
|
||||
Because there is no policy yet, dom0 will ask you to create one (of cource you
|
||||
can do it before the first run of our_test_add_client). So, in dom0, create (by now,
|
||||
with a file editor) the /etc/qubes_rpc/policy/test.Add file with
|
||||
anyvm anyvm ask
|
||||
content. The format of the /etc/qubes_rpc/policy/* files is
|
||||
srcvm destvm (allow|deny|ask)[,user=user_to_run_as][,target=VM_to_redirect_to]
|
||||
|
||||
You can specify srcvm and destvm by name, or by one of "anyvm", "dispvm", "dom0"
|
||||
reserved keywords.
|
||||
Then, when you confirm the operation, you will get the result in the client vm.
|
||||
|
@ -1,314 +0,0 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include "qrexec.h"
|
||||
#include "libqrexec-utils.h"
|
||||
|
||||
// whether qrexec-client should replace ESC with _ before printing the output
|
||||
int replace_esc_stdout = 0;
|
||||
int replace_esc_stderr = 0;
|
||||
|
||||
int connect_unix_socket(const char *domname)
|
||||
{
|
||||
int s, len;
|
||||
struct sockaddr_un remote;
|
||||
|
||||
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
remote.sun_family = AF_UNIX;
|
||||
snprintf(remote.sun_path, sizeof remote.sun_path,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname);
|
||||
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
|
||||
if (connect(s, (struct sockaddr *) &remote, len) == -1) {
|
||||
perror("connect");
|
||||
exit(1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void do_exec(const char *prog)
|
||||
{
|
||||
execl("/bin/bash", "bash", "-c", prog, NULL);
|
||||
}
|
||||
|
||||
static int local_stdin_fd, local_stdout_fd;
|
||||
|
||||
void do_exit(int code)
|
||||
{
|
||||
int status;
|
||||
// sever communication lines; wait for child, if any
|
||||
// so that qrexec-daemon can count (recursively) spawned processes correctly
|
||||
close(local_stdin_fd);
|
||||
close(local_stdout_fd);
|
||||
waitpid(-1, &status, 0);
|
||||
exit(code);
|
||||
}
|
||||
|
||||
|
||||
void prepare_local_fds(const char *cmdline)
|
||||
{
|
||||
int pid;
|
||||
if (!cmdline) {
|
||||
local_stdin_fd = 1;
|
||||
local_stdout_fd = 0;
|
||||
return;
|
||||
}
|
||||
do_fork_exec(cmdline, &pid, &local_stdin_fd, &local_stdout_fd,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
void send_cmdline(int s, int type, const char *cmdline)
|
||||
{
|
||||
struct client_header hdr;
|
||||
hdr.type = type;
|
||||
hdr.len = strlen(cmdline) + 1;
|
||||
if (!write_all(s, &hdr, sizeof(hdr))
|
||||
|| !write_all(s, cmdline, hdr.len)) {
|
||||
perror("write daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_input(int s)
|
||||
{
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
int ret;
|
||||
ret = read(local_stdout_fd, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
perror("read");
|
||||
do_exit(1);
|
||||
}
|
||||
if (ret == 0) {
|
||||
close(local_stdout_fd);
|
||||
local_stdout_fd = -1;
|
||||
shutdown(s, SHUT_WR);
|
||||
if (local_stdin_fd == -1) {
|
||||
// if pipe in opposite direction already closed, no need to stay alive
|
||||
do_exit(0);
|
||||
}
|
||||
}
|
||||
if (!write_all(s, buf, ret)) {
|
||||
if (errno == EPIPE) {
|
||||
// daemon disconnected its end of socket, so no future data will be
|
||||
// send there; there is no sense to read from child stdout
|
||||
//
|
||||
// since AF_UNIX socket is buffered it doesn't mean all data was
|
||||
// received from the agent
|
||||
close(local_stdout_fd);
|
||||
local_stdout_fd = -1;
|
||||
if (local_stdin_fd == -1) {
|
||||
// since child does no longer accept data on its stdin, doesn't
|
||||
// make sense to process the data from the daemon
|
||||
//
|
||||
// we don't know real exit VM process code (exiting here, before
|
||||
// MSG_SERVER_TO_CLIENT_EXIT_CODE message)
|
||||
do_exit(1);
|
||||
}
|
||||
} else
|
||||
perror("write daemon");
|
||||
}
|
||||
}
|
||||
|
||||
void do_replace_esc(char *buf, int len) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (buf[i] == '\033')
|
||||
buf[i] = '_';
|
||||
}
|
||||
|
||||
void handle_daemon_data(int s)
|
||||
{
|
||||
int status;
|
||||
struct client_header hdr;
|
||||
char buf[MAX_DATA_CHUNK], *bufptr=buf;
|
||||
|
||||
if (!read_all(s, &hdr, sizeof hdr)) {
|
||||
perror("read daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
if (hdr.len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "client_header.len=%d\n", hdr.len);
|
||||
do_exit(1);
|
||||
}
|
||||
if (!read_all(s, buf, hdr.len)) {
|
||||
perror("read daemon");
|
||||
do_exit(1);
|
||||
}
|
||||
|
||||
switch (hdr.type) {
|
||||
case MSG_SERVER_TO_CLIENT_STDOUT:
|
||||
if (replace_esc_stdout)
|
||||
do_replace_esc(buf, hdr.len);
|
||||
if (local_stdin_fd == -1)
|
||||
break;
|
||||
if (hdr.len == 0) {
|
||||
close(local_stdin_fd);
|
||||
local_stdin_fd = -1;
|
||||
} else if (!write_all(local_stdin_fd, buf, hdr.len)) {
|
||||
if (errno == EPIPE) {
|
||||
// remote side have closed its stdin, handle data in oposite
|
||||
// direction (if any) before exit
|
||||
local_stdin_fd = -1;
|
||||
} else {
|
||||
perror("write local stdout");
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_STDERR:
|
||||
if (replace_esc_stderr)
|
||||
do_replace_esc(buf, hdr.len);
|
||||
write_all(2, buf, hdr.len);
|
||||
break;
|
||||
case MSG_SERVER_TO_CLIENT_EXIT_CODE:
|
||||
status = *(unsigned int *) bufptr;
|
||||
if (WIFEXITED(status))
|
||||
do_exit(WEXITSTATUS(status));
|
||||
else
|
||||
do_exit(255);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown msg %d\n", hdr.type);
|
||||
do_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// perhaps we could save a syscall if we include both sides in both
|
||||
// rdset and wrset; to be investigated
|
||||
void handle_daemon_only_until_writable(int s)
|
||||
{
|
||||
fd_set rdset, wrset;
|
||||
|
||||
do {
|
||||
FD_ZERO(&rdset);
|
||||
FD_ZERO(&wrset);
|
||||
FD_SET(s, &rdset);
|
||||
FD_SET(s, &wrset);
|
||||
|
||||
if (select(s + 1, &rdset, &wrset, NULL, NULL) < 0) {
|
||||
perror("select");
|
||||
do_exit(1);
|
||||
}
|
||||
if (FD_ISSET(s, &rdset))
|
||||
handle_daemon_data(s);
|
||||
} while (!FD_ISSET(s, &wrset));
|
||||
}
|
||||
|
||||
void *input_process_loop(void *arg) {
|
||||
int s = *(int*)arg;
|
||||
while (local_stdout_fd != -1)
|
||||
handle_input(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void select_loop(int s)
|
||||
{
|
||||
pthread_t input_thread;
|
||||
if (pthread_create(&input_thread, NULL, input_process_loop, &s) != 0) {
|
||||
perror("pthread_create");
|
||||
do_exit(1);
|
||||
}
|
||||
for (;;) {
|
||||
handle_daemon_data(s);
|
||||
}
|
||||
pthread_join(input_thread, NULL);
|
||||
}
|
||||
|
||||
void usage(const char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s -d domain_num [-l local_prog] -e -t -T -c remote_cmdline\n"
|
||||
"-e means exit after sending cmd, -c: connect to existing process\n"
|
||||
"-t enables replacing ESC character with '_' in command output, -T is the same for stderr\n",
|
||||
name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
char *domname = NULL;
|
||||
int s;
|
||||
int just_exec = 0;
|
||||
int connect_existing = 0;
|
||||
char *local_cmdline = NULL;
|
||||
while ((opt = getopt(argc, argv, "d:l:ectT")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domname = strdup(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
local_cmdline = strdup(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
just_exec = 1;
|
||||
break;
|
||||
case 'c':
|
||||
connect_existing = 1;
|
||||
break;
|
||||
case 't':
|
||||
replace_esc_stdout = 1;
|
||||
break;
|
||||
case 'T':
|
||||
replace_esc_stderr = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
}
|
||||
if (optind >= argc || !domname)
|
||||
usage(argv[0]);
|
||||
|
||||
register_exec_func(&do_exec);
|
||||
|
||||
s = connect_unix_socket(domname);
|
||||
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
||||
prepare_local_fds(local_cmdline);
|
||||
|
||||
if (just_exec)
|
||||
send_cmdline(s, MSG_CLIENT_TO_SERVER_JUST_EXEC,
|
||||
argv[optind]);
|
||||
else {
|
||||
int cmd;
|
||||
if (connect_existing)
|
||||
cmd = MSG_CLIENT_TO_SERVER_CONNECT_EXISTING;
|
||||
else
|
||||
cmd = MSG_CLIENT_TO_SERVER_EXEC_CMDLINE;
|
||||
send_cmdline(s, cmd, argv[optind]);
|
||||
select_loop(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1,717 +0,0 @@
|
||||
/*
|
||||
* The Qubes OS Project, http://www.qubes-os.org
|
||||
*
|
||||
* Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include "qrexec.h"
|
||||
#include "libqrexec-utils.h"
|
||||
|
||||
enum client_flags {
|
||||
CLIENT_INVALID = 0, // table slot not used
|
||||
CLIENT_CMDLINE = 1, // waiting for cmdline from client
|
||||
CLIENT_DATA = 2, // waiting for data from client
|
||||
CLIENT_DONT_READ = 4, // don't read from the client, the other side pipe is full, or EOF (additionally marked with CLIENT_EOF)
|
||||
CLIENT_OUTQ_FULL = 8, // don't write to client, its stdin pipe is full
|
||||
CLIENT_EOF = 16, // got EOF
|
||||
CLIENT_EXITED = 32 // only send remaining data from client and remove from list
|
||||
};
|
||||
|
||||
struct _client {
|
||||
int state; // combination of above enum client_flags
|
||||
struct buffer buffer; // buffered data to client, if any
|
||||
};
|
||||
|
||||
/*
|
||||
The "clients" array is indexed by client's fd.
|
||||
Thus its size must be equal MAX_FDS; defining MAX_CLIENTS for clarity.
|
||||
*/
|
||||
|
||||
#define MAX_CLIENTS MAX_FDS
|
||||
struct _client clients[MAX_CLIENTS]; // data on all qrexec_client connections
|
||||
|
||||
int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table
|
||||
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
|
||||
const char *default_user = "user";
|
||||
const char default_user_keyword[] = "DEFAULT:";
|
||||
#define default_user_keyword_len_without_colon (sizeof(default_user_keyword)-2)
|
||||
|
||||
int opt_quiet = 0;
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
|
||||
#else
|
||||
# define UNUSED(x) UNUSED_ ## x
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
we need to track the number of children, so that excessive QREXEC_EXECUTE_*
|
||||
commands do not fork-bomb dom0
|
||||
*/
|
||||
volatile int children_count;
|
||||
|
||||
void sigusr1_handler(int UNUSED(x))
|
||||
{
|
||||
if (!opt_quiet)
|
||||
fprintf(stderr, "connected\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void sigchld_parent_handler(int UNUSED(x))
|
||||
{
|
||||
children_count--;
|
||||
/* starting value is 0 so we see dead real qrexec-daemon as -1 */
|
||||
if (children_count < 0) {
|
||||
if (!opt_quiet)
|
||||
fprintf(stderr, "failed\n");
|
||||
else
|
||||
fprintf(stderr, "Connection to the VM failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void sigchld_handler(int x);
|
||||
|
||||
const char *remote_domain_name; // guess what
|
||||
int remote_domain_xid; // guess what
|
||||
|
||||
void unlink_qrexec_socket()
|
||||
{
|
||||
char socket_address[40];
|
||||
char link_to_socket_name[strlen(remote_domain_name) + sizeof(socket_address)];
|
||||
|
||||
snprintf(socket_address, sizeof(socket_address),
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", remote_domain_xid);
|
||||
snprintf(link_to_socket_name, sizeof link_to_socket_name,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", remote_domain_name);
|
||||
unlink(socket_address);
|
||||
unlink(link_to_socket_name);
|
||||
}
|
||||
|
||||
int create_qrexec_socket(int domid, const char *domname)
|
||||
{
|
||||
char socket_address[40];
|
||||
char link_to_socket_name[strlen(domname) + sizeof(socket_address)];
|
||||
|
||||
snprintf(socket_address, sizeof(socket_address),
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%d", domid);
|
||||
snprintf(link_to_socket_name, sizeof link_to_socket_name,
|
||||
QREXEC_DAEMON_SOCKET_DIR "/qrexec.%s", domname);
|
||||
unlink(link_to_socket_name);
|
||||
if (symlink(socket_address, link_to_socket_name)) {
|
||||
fprintf(stderr, "symlink(%s,%s) failed: %s\n", socket_address,
|
||||
link_to_socket_name, strerror (errno));
|
||||
}
|
||||
atexit(unlink_qrexec_socket);
|
||||
return get_server_socket(socket_address);
|
||||
}
|
||||
|
||||
#define MAX_STARTUP_TIME_DEFAULT 60
|
||||
|
||||
/* do the preparatory tasks, needed before entering the main event loop */
|
||||
void init(int xid)
|
||||
{
|
||||
char qrexec_error_log_name[256];
|
||||
int logfd;
|
||||
int i;
|
||||
pid_t pid;
|
||||
int startup_timeout = MAX_STARTUP_TIME_DEFAULT;
|
||||
const char *startup_timeout_str = NULL;
|
||||
|
||||
if (xid <= 0) {
|
||||
fprintf(stderr, "domain id=0?\n");
|
||||
exit(1);
|
||||
}
|
||||
startup_timeout_str = getenv("QREXEC_STARTUP_TIMEOUT");
|
||||
if (startup_timeout_str) {
|
||||
startup_timeout = atoi(startup_timeout_str);
|
||||
if (startup_timeout <= 0)
|
||||
// invalid or negative number
|
||||
startup_timeout = MAX_STARTUP_TIME_DEFAULT;
|
||||
}
|
||||
signal(SIGUSR1, sigusr1_handler);
|
||||
signal(SIGCHLD, sigchld_parent_handler);
|
||||
switch (pid=fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
if (getenv("QREXEC_STARTUP_NOWAIT"))
|
||||
exit(0);
|
||||
if (!opt_quiet)
|
||||
fprintf(stderr, "Waiting for VM's qrexec agent.");
|
||||
for (i=0;i<startup_timeout;i++) {
|
||||
sleep(1);
|
||||
if (!opt_quiet)
|
||||
fprintf(stderr, ".");
|
||||
if (i==startup_timeout-1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Cannot connect to qrexec agent for %d seconds, still trying in the background\n", startup_timeout);
|
||||
exit(1);
|
||||
}
|
||||
close(0);
|
||||
snprintf(qrexec_error_log_name, sizeof(qrexec_error_log_name),
|
||||
"/var/log/qubes/qrexec.%s.log", remote_domain_name);
|
||||
umask(0007); // make the log readable by the "qubes" group
|
||||
logfd =
|
||||
open(qrexec_error_log_name, O_WRONLY | O_CREAT | O_TRUNC,
|
||||
0640);
|
||||
|
||||
if (logfd < 0) {
|
||||
perror("open");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dup2(logfd, 1);
|
||||
dup2(logfd, 2);
|
||||
|
||||
chdir("/var/run/qubes");
|
||||
if (setsid() < 0) {
|
||||
perror("setsid()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
peer_client_init(xid, REXEC_PORT);
|
||||
if (setgid(getgid()) < 0) {
|
||||
perror("setgid()");
|
||||
exit(1);
|
||||
}
|
||||
if (setuid(getuid()) < 0) {
|
||||
perror("setuid()");
|
||||
exit(1);
|
||||
}
|
||||
/* When running as root, make the socket accessible; perms on /var/run/qubes still apply */
|
||||
umask(0);
|
||||
qrexec_daemon_unix_socket_fd =
|
||||
create_qrexec_socket(xid, remote_domain_name);
|
||||
umask(0077);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
signal(SIGUSR1, SIG_DFL);
|
||||
kill(getppid(), SIGUSR1); // let the parent know we are ready
|
||||
}
|
||||
|
||||
void handle_new_client(void)
|
||||
{
|
||||
int fd = do_accept(qrexec_daemon_unix_socket_fd);
|
||||
if (fd >= MAX_CLIENTS) {
|
||||
fprintf(stderr, "too many clients ?\n");
|
||||
exit(1);
|
||||
}
|
||||
clients[fd].state = CLIENT_CMDLINE;
|
||||
buffer_init(&clients[fd].buffer);
|
||||
if (fd > max_client_fd)
|
||||
max_client_fd = fd;
|
||||
}
|
||||
|
||||
void terminate_client_and_flush_data(int fd)
|
||||
{
|
||||
int i;
|
||||
struct server_header s_hdr;
|
||||
|
||||
if (!(clients[fd].state & CLIENT_EXITED) && fork_and_flush_stdin(fd, &clients[fd].buffer))
|
||||
children_count++;
|
||||
close(fd);
|
||||
clients[fd].state = CLIENT_INVALID;
|
||||
buffer_free(&clients[fd].buffer);
|
||||
if (max_client_fd == fd) {
|
||||
for (i = fd; i >= 0 && clients[i].state == CLIENT_INVALID;
|
||||
i--);
|
||||
max_client_fd = i;
|
||||
}
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_CLIENT_END;
|
||||
s_hdr.client_id = fd;
|
||||
s_hdr.len = 0;
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
}
|
||||
|
||||
int get_cmdline_body_from_client_and_pass_to_agent(int fd, struct server_header
|
||||
*s_hdr)
|
||||
{
|
||||
int len = s_hdr->len;
|
||||
char buf[len];
|
||||
int use_default_user = 0;
|
||||
if (!read_all(fd, buf, len)) {
|
||||
terminate_client_and_flush_data(fd);
|
||||
return 0;
|
||||
}
|
||||
if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) {
|
||||
use_default_user = 1;
|
||||
s_hdr->len -= default_user_keyword_len_without_colon; // -1 because of colon
|
||||
s_hdr->len += strlen(default_user);
|
||||
}
|
||||
write_all_vchan_ext(s_hdr, sizeof(*s_hdr));
|
||||
if (use_default_user) {
|
||||
write_all_vchan_ext(default_user, strlen(default_user));
|
||||
write_all_vchan_ext(buf+default_user_keyword_len_without_colon, len-default_user_keyword_len_without_colon);
|
||||
} else
|
||||
write_all_vchan_ext(buf, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void handle_cmdline_message_from_client(int fd)
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr;
|
||||
if (!read_all(fd, &hdr, sizeof hdr)) {
|
||||
terminate_client_and_flush_data(fd);
|
||||
return;
|
||||
}
|
||||
switch (hdr.type) {
|
||||
case MSG_CLIENT_TO_SERVER_EXEC_CMDLINE:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_EXEC_CMDLINE;
|
||||
break;
|
||||
case MSG_CLIENT_TO_SERVER_JUST_EXEC:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_JUST_EXEC;
|
||||
break;
|
||||
case MSG_CLIENT_TO_SERVER_CONNECT_EXISTING:
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_CONNECT_EXISTING;
|
||||
break;
|
||||
default:
|
||||
terminate_client_and_flush_data(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
s_hdr.client_id = fd;
|
||||
s_hdr.len = hdr.len;
|
||||
if (!get_cmdline_body_from_client_and_pass_to_agent(fd, &s_hdr))
|
||||
// client disconnected while sending cmdline, above call already
|
||||
// cleaned up client info
|
||||
return;
|
||||
clients[fd].state = CLIENT_DATA;
|
||||
set_nonblock(fd); // so that we can detect full queue without blocking
|
||||
if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC)
|
||||
terminate_client_and_flush_data(fd);
|
||||
|
||||
}
|
||||
|
||||
/* handle data received from one of qrexec_client processes */
|
||||
void handle_message_from_client(int fd)
|
||||
{
|
||||
struct server_header s_hdr;
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
unsigned int len;
|
||||
int ret;
|
||||
|
||||
if (clients[fd].state == CLIENT_CMDLINE) {
|
||||
handle_cmdline_message_from_client(fd);
|
||||
return;
|
||||
}
|
||||
// We have already passed cmdline from client.
|
||||
// Now the client passes us raw data from its stdin.
|
||||
len = buffer_space_vchan_ext();
|
||||
if (len <= sizeof s_hdr)
|
||||
return;
|
||||
/* Read at most the amount of data that we have room for in vchan */
|
||||
ret = read(fd, buf, len - sizeof(s_hdr));
|
||||
if (ret < 0) {
|
||||
perror("read client");
|
||||
terminate_client_and_flush_data(fd);
|
||||
return;
|
||||
}
|
||||
s_hdr.client_id = fd;
|
||||
s_hdr.len = ret;
|
||||
s_hdr.type = MSG_SERVER_TO_AGENT_INPUT;
|
||||
|
||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||
write_all_vchan_ext(buf, ret);
|
||||
if (ret == 0) // EOF - so don't select() on this client
|
||||
clients[fd].state |= CLIENT_DONT_READ | CLIENT_EOF;
|
||||
if (clients[fd].state & CLIENT_EXITED)
|
||||
//client already exited and all data sent - cleanup now
|
||||
terminate_client_and_flush_data(fd);
|
||||
}
|
||||
|
||||
/*
|
||||
Called when there is buffered data for this client, and select() reports
|
||||
that client's pipe is writable; so we should be able to flush some
|
||||
buffered data.
|
||||
*/
|
||||
void write_buffered_data_to_client(int client_id)
|
||||
{
|
||||
switch (flush_client_data
|
||||
(client_id, client_id, &clients[client_id].buffer)) {
|
||||
case WRITE_STDIN_OK: // no more buffered data
|
||||
clients[client_id].state &= ~CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
// do not write to this fd anymore
|
||||
clients[client_id].state |= CLIENT_EXITED;
|
||||
if (clients[client_id].state & CLIENT_EOF)
|
||||
terminate_client_and_flush_data(client_id);
|
||||
else
|
||||
// client will be removed when read returns 0 (EOF)
|
||||
// clear CLIENT_OUTQ_FULL flag to no select on this fd anymore
|
||||
clients[client_id].state &= ~CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown flush_client_data?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The header (hdr argument) is already built. Just read the raw data from
|
||||
the packet, and pass it along with the header to the client.
|
||||
*/
|
||||
void get_packet_data_from_agent_and_pass_to_client(int client_id, struct client_header
|
||||
*hdr)
|
||||
{
|
||||
int len = hdr->len;
|
||||
char buf[sizeof(*hdr) + len];
|
||||
|
||||
/* make both the header and data be consecutive in the buffer */
|
||||
memcpy(buf, hdr, sizeof(*hdr));
|
||||
read_all_vchan_ext(buf + sizeof(*hdr), len);
|
||||
if (clients[client_id].state & CLIENT_EXITED)
|
||||
// ignore data for no longer running client
|
||||
return;
|
||||
|
||||
switch (write_stdin
|
||||
(client_id, client_id, buf, len + sizeof(*hdr),
|
||||
&clients[client_id].buffer)) {
|
||||
case WRITE_STDIN_OK:
|
||||
break;
|
||||
case WRITE_STDIN_BUFFERED: // some data have been buffered
|
||||
clients[client_id].state |= CLIENT_OUTQ_FULL;
|
||||
break;
|
||||
case WRITE_STDIN_ERROR:
|
||||
// do not write to this fd anymore
|
||||
clients[client_id].state |= CLIENT_EXITED;
|
||||
// if already got EOF, remove client
|
||||
if (clients[client_id].state & CLIENT_EOF)
|
||||
terminate_client_and_flush_data(client_id);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown write_stdin?\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
The signal handler executes asynchronously; therefore all it should do is
|
||||
to set a flag "signal has arrived", and let the main even loop react to this
|
||||
flag in appropriate moment.
|
||||
*/
|
||||
|
||||
int child_exited;
|
||||
|
||||
void sigchld_handler(int UNUSED(x))
|
||||
{
|
||||
child_exited = 1;
|
||||
signal(SIGCHLD, sigchld_handler);
|
||||
}
|
||||
|
||||
/* clean zombies, update children_count */
|
||||
void reap_children(void)
|
||||
{
|
||||
int status;
|
||||
while (waitpid(-1, &status, WNOHANG) > 0)
|
||||
children_count--;
|
||||
child_exited = 0;
|
||||
}
|
||||
|
||||
/* too many children - wait for one of them to terminate */
|
||||
void wait_for_child(void)
|
||||
{
|
||||
int status;
|
||||
waitpid(-1, &status, 0);
|
||||
children_count--;
|
||||
}
|
||||
|
||||
#define MAX_CHILDREN 10
|
||||
void check_children_count_and_wait_if_too_many(void)
|
||||
{
|
||||
if (children_count > MAX_CHILDREN) {
|
||||
fprintf(stderr,
|
||||
"max number of children reached, waiting for child exit...\n");
|
||||
wait_for_child();
|
||||
fprintf(stderr, "now children_count=%d, continuing.\n",
|
||||
children_count);
|
||||
}
|
||||
}
|
||||
|
||||
void sanitize_name(char * untrusted_s_signed)
|
||||
{
|
||||
unsigned char * untrusted_s;
|
||||
for (untrusted_s=(unsigned char*)untrusted_s_signed; *untrusted_s; untrusted_s++) {
|
||||
if (*untrusted_s >= 'a' && *untrusted_s <= 'z')
|
||||
continue;
|
||||
if (*untrusted_s >= 'A' && *untrusted_s <= 'Z')
|
||||
continue;
|
||||
if (*untrusted_s >= '0' && *untrusted_s <= '9')
|
||||
continue;
|
||||
if (*untrusted_s == '$' || *untrusted_s == '_' || *untrusted_s == '-' || *untrusted_s == '.' || *untrusted_s == ' ')
|
||||
continue;
|
||||
*untrusted_s = '_';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define ENSURE_NULL_TERMINATED(x) x[sizeof(x)-1] = 0
|
||||
|
||||
/*
|
||||
Called when agent sends a message asking to execute a predefined command.
|
||||
*/
|
||||
|
||||
void handle_execute_predefined_command(void)
|
||||
{
|
||||
int i;
|
||||
struct trigger_connect_params untrusted_params, params;
|
||||
|
||||
check_children_count_and_wait_if_too_many();
|
||||
read_all_vchan_ext(&untrusted_params, sizeof(params));
|
||||
|
||||
/* sanitize start */
|
||||
ENSURE_NULL_TERMINATED(untrusted_params.exec_index);
|
||||
ENSURE_NULL_TERMINATED(untrusted_params.target_vmname);
|
||||
ENSURE_NULL_TERMINATED(untrusted_params.process_fds.ident);
|
||||
sanitize_name(untrusted_params.exec_index);
|
||||
sanitize_name(untrusted_params.target_vmname);
|
||||
sanitize_name(untrusted_params.process_fds.ident);
|
||||
params = untrusted_params;
|
||||
/* sanitize end */
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
children_count++;
|
||||
return;
|
||||
}
|
||||
for (i = 3; i < MAX_FDS; i++)
|
||||
close(i);
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
execl("/usr/lib/qubes/qrexec-policy", "qrexec-policy",
|
||||
remote_domain_name, params.target_vmname,
|
||||
params.exec_index, params.process_fds.ident, NULL);
|
||||
perror("execl");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
void check_client_id_in_range(unsigned int untrusted_client_id)
|
||||
{
|
||||
if (untrusted_client_id >= MAX_CLIENTS) {
|
||||
fprintf(stderr, "from agent: client_id=%d\n",
|
||||
untrusted_client_id);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void sanitize_message_from_agent(struct server_header *untrusted_header)
|
||||
{
|
||||
switch (untrusted_header->type) {
|
||||
case MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING:
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_STDOUT:
|
||||
case MSG_AGENT_TO_SERVER_STDERR:
|
||||
case MSG_AGENT_TO_SERVER_EXIT_CODE:
|
||||
check_client_id_in_range(untrusted_header->client_id);
|
||||
if (untrusted_header->len > MAX_DATA_CHUNK) {
|
||||
fprintf(stderr, "agent feeded %d of data bytes?\n",
|
||||
untrusted_header->len);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case MSG_XOFF:
|
||||
case MSG_XON:
|
||||
check_client_id_in_range(untrusted_header->client_id);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown mesage type %d from agent\n",
|
||||
untrusted_header->type);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_message_from_agent(void)
|
||||
{
|
||||
struct client_header hdr;
|
||||
struct server_header s_hdr, untrusted_s_hdr;
|
||||
|
||||
read_all_vchan_ext(&untrusted_s_hdr, sizeof untrusted_s_hdr);
|
||||
/* sanitize start */
|
||||
sanitize_message_from_agent(&untrusted_s_hdr);
|
||||
s_hdr = untrusted_s_hdr;
|
||||
/* sanitize end */
|
||||
|
||||
// fprintf(stderr, "got %x %x %x\n", s_hdr.type, s_hdr.client_id,
|
||||
// s_hdr.len);
|
||||
|
||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_TRIGGER_CONNECT_EXISTING) {
|
||||
handle_execute_predefined_command();
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_hdr.type == MSG_XOFF) {
|
||||
clients[s_hdr.client_id].state |= CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_hdr.type == MSG_XON) {
|
||||
clients[s_hdr.client_id].state &= ~CLIENT_DONT_READ;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (s_hdr.type) {
|
||||
case MSG_AGENT_TO_SERVER_STDOUT:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDOUT;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_STDERR:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_STDERR;
|
||||
break;
|
||||
case MSG_AGENT_TO_SERVER_EXIT_CODE:
|
||||
hdr.type = MSG_SERVER_TO_CLIENT_EXIT_CODE;
|
||||
break;
|
||||
default: /* cannot happen, already sanitized */
|
||||
fprintf(stderr, "from agent: type=%d\n", s_hdr.type);
|
||||
exit(1);
|
||||
}
|
||||
hdr.len = s_hdr.len;
|
||||
if (clients[s_hdr.client_id].state == CLIENT_INVALID) {
|
||||
// benefit of doubt - maybe client exited earlier
|
||||
// just eat the packet data and continue
|
||||
char buf[MAX_DATA_CHUNK];
|
||||
read_all_vchan_ext(buf, s_hdr.len);
|
||||
return;
|
||||
}
|
||||
get_packet_data_from_agent_and_pass_to_client(s_hdr.client_id,
|
||||
&hdr);
|
||||
if (s_hdr.type == MSG_AGENT_TO_SERVER_EXIT_CODE)
|
||||
terminate_client_and_flush_data(s_hdr.client_id);
|
||||
}
|
||||
|
||||
/*
|
||||
Scan the "clients" table, add ones we want to read from (because the other
|
||||
end has not send MSG_XOFF on them) to read_fdset, add ones we want to write
|
||||
to (because its pipe is full) to write_fdset. Return the highest used file
|
||||
descriptor number, needed for the first select() parameter.
|
||||
*/
|
||||
int fill_fdsets_for_select(fd_set * read_fdset, fd_set * write_fdset)
|
||||
{
|
||||
int i;
|
||||
int max = -1;
|
||||
FD_ZERO(read_fdset);
|
||||
FD_ZERO(write_fdset);
|
||||
for (i = 0; i <= max_client_fd; i++) {
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& !(clients[i].state & CLIENT_DONT_READ)) {
|
||||
FD_SET(i, read_fdset);
|
||||
max = i;
|
||||
}
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& clients[i].state & CLIENT_OUTQ_FULL) {
|
||||
FD_SET(i, write_fdset);
|
||||
max = i;
|
||||
}
|
||||
}
|
||||
FD_SET(qrexec_daemon_unix_socket_fd, read_fdset);
|
||||
if (qrexec_daemon_unix_socket_fd > max)
|
||||
max = qrexec_daemon_unix_socket_fd;
|
||||
return max;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
fd_set read_fdset, write_fdset;
|
||||
int i, opt;
|
||||
int max;
|
||||
sigset_t chld_set;
|
||||
|
||||
while ((opt=getopt(argc, argv, "q")) != -1) {
|
||||
switch (opt) {
|
||||
case 'q':
|
||||
opt_quiet = 1;
|
||||
break;
|
||||
default: /* '?' */
|
||||
fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (argc - optind < 2 || argc - optind > 3) {
|
||||
fprintf(stderr, "usage: %s [-q] domainid domain-name [default user]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
remote_domain_name = argv[optind+1];
|
||||
if (argc - optind >= 3)
|
||||
default_user = argv[optind+2];
|
||||
remote_domain_xid = atoi(argv[optind]);
|
||||
init(remote_domain_xid);
|
||||
sigemptyset(&chld_set);
|
||||
sigaddset(&chld_set, SIGCHLD);
|
||||
/*
|
||||
The main event loop. Waits for one of the following events:
|
||||
- message from client
|
||||
- message from agent
|
||||
- new client
|
||||
- child exited
|
||||
*/
|
||||
for (;;) {
|
||||
max = fill_fdsets_for_select(&read_fdset, &write_fdset);
|
||||
if (buffer_space_vchan_ext() <=
|
||||
sizeof(struct server_header))
|
||||
FD_ZERO(&read_fdset); // vchan full - don't read from clients
|
||||
|
||||
sigprocmask(SIG_BLOCK, &chld_set, NULL);
|
||||
if (child_exited)
|
||||
reap_children();
|
||||
wait_for_vchan_or_argfd(max, &read_fdset, &write_fdset);
|
||||
sigprocmask(SIG_UNBLOCK, &chld_set, NULL);
|
||||
|
||||
if (FD_ISSET(qrexec_daemon_unix_socket_fd, &read_fdset))
|
||||
handle_new_client();
|
||||
|
||||
while (read_ready_vchan_ext())
|
||||
handle_message_from_agent();
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &read_fdset))
|
||||
handle_message_from_client(i);
|
||||
|
||||
for (i = 0; i <= max_client_fd; i++)
|
||||
if (clients[i].state != CLIENT_INVALID
|
||||
&& FD_ISSET(i, &write_fdset))
|
||||
write_buffered_data_to_client(i);
|
||||
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import xen.lowlevel.xl
|
||||
import qubes.guihelpers
|
||||
from optparse import OptionParser
|
||||
import fcntl
|
||||
|
||||
POLICY_FILE_DIR="/etc/qubes-rpc/policy"
|
||||
# XXX: Backward compatibility, to be removed soon
|
||||
DEPRECATED_POLICY_FILE_DIR="/etc/qubes_rpc/policy"
|
||||
QREXEC_CLIENT="/usr/lib/qubes/qrexec-client"
|
||||
|
||||
class UserChoice:
|
||||
ALLOW=0
|
||||
DENY=1
|
||||
ALWAYS_ALLOW=2
|
||||
|
||||
def line_to_dict(line):
|
||||
tokens=line.split()
|
||||
if len(tokens) < 3:
|
||||
return None
|
||||
|
||||
if tokens[0][0] == '#':
|
||||
return None
|
||||
|
||||
dict={}
|
||||
dict['source']=tokens[0]
|
||||
dict['dest']=tokens[1]
|
||||
|
||||
dict['full-action']=tokens[2]
|
||||
action_list=tokens[2].split(',')
|
||||
dict['action']=action_list.pop(0)
|
||||
|
||||
for iter in action_list:
|
||||
paramval=iter.split("=")
|
||||
dict["action."+paramval[0]]=paramval[1]
|
||||
|
||||
# Warn if we're ignoring extra data after a space, such as:
|
||||
# vm1 vm2 allow, user=foo
|
||||
if len(tokens) > 3:
|
||||
print >>sys.stderr, "Trailing data ignored in %s" % line
|
||||
|
||||
return dict
|
||||
|
||||
|
||||
def read_policy_file(exec_index):
|
||||
policy_file=POLICY_FILE_DIR+"/"+exec_index
|
||||
if not os.path.isfile(policy_file):
|
||||
policy_file=DEPRECATED_POLICY_FILE_DIR+"/"+exec_index
|
||||
if not os.path.isfile(policy_file):
|
||||
return None
|
||||
print >>sys.stderr, "RPC service '%s' uses deprecated policy location, please move to %s" % (exec_index, POLICY_FILE_DIR)
|
||||
policy_list=list()
|
||||
f = open(policy_file)
|
||||
fcntl.flock(f, fcntl.LOCK_SH)
|
||||
for iter in f.readlines():
|
||||
dict = line_to_dict(iter)
|
||||
if dict is not None:
|
||||
policy_list.append(dict)
|
||||
f.close()
|
||||
return policy_list
|
||||
|
||||
def is_match(item, config_term):
|
||||
return (item is not "dom0" and config_term == "$anyvm") or item == config_term
|
||||
|
||||
def get_default_policy():
|
||||
dict={}
|
||||
dict["action"]="deny"
|
||||
return dict
|
||||
|
||||
|
||||
def find_policy(policy, domain, target):
|
||||
for iter in policy:
|
||||
if not is_match(domain, iter["source"]):
|
||||
continue
|
||||
if not is_match(target, iter["dest"]):
|
||||
continue
|
||||
return iter
|
||||
return get_default_policy()
|
||||
|
||||
def is_domain_running(target):
|
||||
xl_ctx = xen.lowlevel.xl.ctx()
|
||||
domains = xl_ctx.list_domains()
|
||||
for dominfo in domains:
|
||||
domname = xl_ctx.domid_to_name(dominfo.domid)
|
||||
if domname == target:
|
||||
return True
|
||||
return False
|
||||
|
||||
def validate_target(target):
|
||||
# special targets
|
||||
if target in ['$dispvm', 'dom0']:
|
||||
return True
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
|
||||
qc = QubesVmCollection()
|
||||
qc.lock_db_for_reading()
|
||||
qc.load()
|
||||
qc.unlock_db()
|
||||
|
||||
return qc.get_vm_by_name(target) is not None
|
||||
|
||||
def spawn_target_if_necessary(target):
|
||||
if is_domain_running(target):
|
||||
return
|
||||
null=open("/dev/null", "r+")
|
||||
subprocess.call(["qvm-run", "-a", "-q", target, "true"], stdin=null, stdout=null)
|
||||
null.close()
|
||||
|
||||
def do_execute(domain, target, user, exec_index, process_ident):
|
||||
if target == "dom0":
|
||||
cmd="/usr/lib/qubes/qubes-rpc-multiplexer "+exec_index + " " + domain
|
||||
elif target == "$dispvm":
|
||||
cmd = "/usr/lib/qubes/qfile-daemon-dvm " + exec_index + " " + domain + " " +user
|
||||
else:
|
||||
# see the previous commit why "qvm-run -a" is broken and dangerous
|
||||
# also, dangling "xl" would keep stderr open and may prevent closing connection
|
||||
spawn_target_if_necessary(target)
|
||||
cmd= QREXEC_CLIENT + " -d " + target + " '" + user
|
||||
cmd+=":QUBESRPC "+ exec_index + " " + domain + "'"
|
||||
# stderr should be logged in source/target VM
|
||||
null = open(os.devnull, 'w')
|
||||
os.dup2(null.fileno(), 2)
|
||||
os.execl(QREXEC_CLIENT, "qrexec-client", "-d", domain, "-l", cmd, "-c", process_ident)
|
||||
|
||||
def confirm_execution(domain, target, exec_index):
|
||||
text = "Do you allow domain \"" +domain + "\" to execute " + exec_index
|
||||
text+= " operation on the domain \"" + target +"\"?<br>"
|
||||
text+= " \"Yes to All\" option will automatically allow this operation in the future."
|
||||
return qubes.guihelpers.ask(text, yestoall=True)
|
||||
|
||||
def add_always_allow(domain, target, exec_index, options):
|
||||
policy_file=POLICY_FILE_DIR+"/"+exec_index
|
||||
if not os.path.isfile(policy_file):
|
||||
return None
|
||||
f = open(policy_file, 'r+')
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
lines = []
|
||||
for l in f.readlines():
|
||||
lines.append(l)
|
||||
lines.insert(0, "%s\t%s\tallow%s\n" % (domain, target, options))
|
||||
f.seek(0)
|
||||
f.write("".join(lines))
|
||||
f.close()
|
||||
|
||||
def policy_editor(domain, target, exec_index):
|
||||
text = "No policy definition found for " + exec_index + " action. "
|
||||
text+= "Please create a policy file in Dom0 in " + POLICY_FILE_DIR + "/" + exec_index
|
||||
subprocess.call(["/usr/bin/zenity", "--info", "--text", text])
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <src-domain> <target-domain> <service> <process-ident>"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False,
|
||||
help="Allow run of service without confirmation if policy say 'ask'")
|
||||
parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False,
|
||||
help="Do not run the service, only evaluate policy; retcode=0 means 'allow'")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
domain=args[0]
|
||||
target=args[1]
|
||||
exec_index=args[2]
|
||||
process_ident=args[3]
|
||||
|
||||
if not validate_target(target):
|
||||
print >> sys.stderr, "Rpc failed (unknown domain):", domain, target, exec_index
|
||||
text = "Domain '%s' doesn't exist (service %s called by domain %s)." % (
|
||||
target, exec_index, domain)
|
||||
subprocess.call(["/usr/bin/zenity", "--error", "--text", text])
|
||||
os.execl(QREXEC_CLIENT, "qrexec-client", "-d", domain, "-l", "/bin/false", "-c", process_ident)
|
||||
|
||||
policy_list=read_policy_file(exec_index)
|
||||
if policy_list==None:
|
||||
policy_editor(domain, target, exec_index)
|
||||
policy_list=read_policy_file(exec_index)
|
||||
if policy_list==None:
|
||||
policy_list=list()
|
||||
|
||||
policy_dict=find_policy(policy_list, domain, target)
|
||||
|
||||
if policy_dict["action"] == "ask" and options.assume_yes_for_ask:
|
||||
policy_dict["action"] = "allow"
|
||||
|
||||
if policy_dict["action"] == "ask":
|
||||
user_choice = confirm_execution(domain, target, exec_index)
|
||||
if user_choice == UserChoice.ALWAYS_ALLOW:
|
||||
add_always_allow(domain, target, exec_index, policy_dict["full-action"].lstrip('ask'))
|
||||
policy_dict["action"] = "allow"
|
||||
elif user_choice == UserChoice.ALLOW:
|
||||
policy_dict["action"] = "allow"
|
||||
else:
|
||||
policy_dict["action"] = "deny"
|
||||
|
||||
if options.just_evaluate:
|
||||
if policy_dict["action"] == "allow":
|
||||
exit(0)
|
||||
else:
|
||||
exit(1)
|
||||
|
||||
if policy_dict["action"] == "allow":
|
||||
if policy_dict.has_key("action.target"):
|
||||
target=policy_dict["action.target"]
|
||||
if policy_dict.has_key("action.user"):
|
||||
user=policy_dict["action.user"]
|
||||
else:
|
||||
user="DEFAULT"
|
||||
print >> sys.stderr, "Rpc allowed:", domain, target, exec_index
|
||||
do_execute(domain, target, user, exec_index, process_ident)
|
||||
|
||||
print >> sys.stderr, "Rpc denied:", domain, target, exec_index
|
||||
os.execl(QREXEC_CLIENT, "qrexec-client", "-d", domain, "-l", "/bin/false", "-c", process_ident)
|
||||
|
||||
main()
|
@ -1,30 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
mkfifo /tmp/qrexec-rpc-stderr.$$
|
||||
logger -t "$1-$2" -f /tmp/qrexec-rpc-stderr.$$ &
|
||||
exec 2>/tmp/qrexec-rpc-stderr.$$
|
||||
rm -f /tmp/qrexec-rpc-stderr.$$
|
||||
|
||||
QUBES_RPC=/etc/qubes-rpc
|
||||
# XXX: Backward compatibility
|
||||
DEPRECATED_QUBES_RPC=/etc/qubes_rpc
|
||||
if ! [ $# = 2 ] ; then
|
||||
echo $0: bad argument count >&2
|
||||
exit 1
|
||||
fi
|
||||
export QREXEC_REMOTE_DOMAIN="$2"
|
||||
CFG_FILE=$QUBES_RPC/"$1"
|
||||
if [ -s "$CFG_FILE" ] ; then
|
||||
exec /bin/sh "$CFG_FILE"
|
||||
echo "$0: failed to execute handler for" "$1" >&2
|
||||
exit 1
|
||||
fi
|
||||
CFG_FILE=$DEPRECATED_QUBES_RPC/"$1"
|
||||
if [ -s "$CFG_FILE" ] ; then
|
||||
echo "$0: RPC service '$1' uses deprecated directory, please move to $QUBES_RPC" >&2
|
||||
exec /bin/sh "$CFG_FILE"
|
||||
echo "$0: failed to execute handler for" "$1" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "$0: nonexistent or empty" "$CFG_FILE" file >&2
|
||||
exit 1
|
7
qubes-rpc-policy/qubes.repos.Disable
Normal file
@ -0,0 +1,7 @@
|
||||
## Note that policy parsing stops at the first match,
|
||||
## so adding anything below "$anyvm $anyvm action" line will have no effect
|
||||
|
||||
## Please use a single # to start your custom comments
|
||||
|
||||
dom0 dom0 allow
|
||||
$anyvm $anyvm deny
|
7
qubes-rpc-policy/qubes.repos.Enable
Normal file
@ -0,0 +1,7 @@
|
||||
## Note that policy parsing stops at the first match,
|
||||
## so adding anything below "$anyvm $anyvm action" line will have no effect
|
||||
|
||||
## Please use a single # to start your custom comments
|
||||
|
||||
dom0 dom0 allow
|
||||
$anyvm $anyvm deny
|
7
qubes-rpc-policy/qubes.repos.List
Normal file
@ -0,0 +1,7 @@
|
||||
## Note that policy parsing stops at the first match,
|
||||
## so adding anything below "$anyvm $anyvm action" line will have no effect
|
||||
|
||||
## Please use a single # to start your custom comments
|
||||
|
||||
dom0 dom0 allow
|
||||
$anyvm $anyvm deny
|
24
qubes-rpc/qubes.repos.Disable
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# `ok` on stdout indicates success; any stderr output indicates an error
|
||||
# (probably an exception)
|
||||
|
||||
import dnf
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.umask(0o022)
|
||||
|
||||
base = dnf.Base()
|
||||
|
||||
base.read_all_repos()
|
||||
|
||||
reponame = sys.argv[1]
|
||||
repo = base.repos[reponame]
|
||||
|
||||
base.conf.write_raw_configfile(repo.repofile,
|
||||
repo.id,
|
||||
base.conf.substitutions,
|
||||
{'enabled': '0'})
|
||||
|
||||
print('ok')
|
24
qubes-rpc/qubes.repos.Enable
Executable file
@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# `ok` on stdout indicates success; any stderr output indicates an error
|
||||
# (probably an exception)
|
||||
|
||||
import dnf
|
||||
import os
|
||||
import sys
|
||||
|
||||
os.umask(0o022)
|
||||
|
||||
base = dnf.Base()
|
||||
|
||||
base.read_all_repos()
|
||||
|
||||
reponame = sys.argv[1]
|
||||
repo = base.repos[reponame]
|
||||
|
||||
base.conf.write_raw_configfile(repo.repofile,
|
||||
repo.id,
|
||||
base.conf.substitutions,
|
||||
{'enabled': '1'})
|
||||
|
||||
print('ok')
|
17
qubes-rpc/qubes.repos.List
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Records in the output are separated by newlines; fields are separated by \0
|
||||
# Each record is unique_id:pretty_name:enabled
|
||||
|
||||
import dnf
|
||||
|
||||
base = dnf.Base()
|
||||
|
||||
base.read_all_repos()
|
||||
|
||||
first = True
|
||||
for repo in base.repos.all():
|
||||
l = [repo.id, repo.name, 'enabled' if repo.enabled else 'disabled']
|
||||
if not first: print()
|
||||
first = False
|
||||
print('\0'.join(l), end='')
|
@ -24,15 +24,10 @@
|
||||
|
||||
%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
|
||||
|
||||
%{!?version: %define version %(cat version)}
|
||||
|
||||
%define _dracutmoddir /usr/lib/dracut/modules.d
|
||||
%if %{fedora} < 17
|
||||
%define _dracutmoddir /usr/share/dracut/modules.d
|
||||
%endif
|
||||
|
||||
Name: qubes-core-dom0-linux
|
||||
Version: %{version}
|
||||
Version: @VERSION@
|
||||
Release: 1%{?dist}
|
||||
Summary: Linux-specific files for Qubes dom0
|
||||
|
||||
@ -43,57 +38,57 @@ URL: http://www.qubes-os.org
|
||||
|
||||
BuildRequires: ImageMagick
|
||||
BuildRequires: pandoc
|
||||
BuildRequires: qubes-utils-devel >= 2.0.5
|
||||
BuildRequires: qubes-utils-devel >= 3.1.3
|
||||
BuildRequires: gcc
|
||||
Requires: qubes-core-dom0
|
||||
Requires: qubes-utils >= 2.0.6
|
||||
Requires: python3-qubesadmin
|
||||
Requires: qubes-core-qrexec-dom0
|
||||
Requires: qubes-core-admin-client
|
||||
Requires: qubes-utils >= 3.1.3
|
||||
Requires: qubes-utils-libs >= 4.0.16
|
||||
Conflicts: qubes-core-dom0 < 4.0.23
|
||||
Requires: %{name}-kernel-install
|
||||
Requires: xdotool
|
||||
Requires: createrepo_c
|
||||
|
||||
%define _builddir %(pwd)
|
||||
Source0: %{name}-%{version}.tar.gz
|
||||
|
||||
%description
|
||||
Linux customizations required to use system as Qubes dom0.
|
||||
Additionally some graphical elements for every Linux desktop envirnment (icons,
|
||||
appmenus etc).
|
||||
|
||||
%package kernel-install
|
||||
Summary: Kernel install hook for Xen-based system
|
||||
|
||||
# get rid of os-prober, it tries to mount and parse all the block devices in
|
||||
# the system, including loop*
|
||||
Provides: os-prober
|
||||
Obsoletes: os-prober
|
||||
|
||||
%description kernel-install
|
||||
Kernel install hook for Xen-based system.
|
||||
|
||||
%prep
|
||||
# we operate on the current directory, so no need to unpack anything
|
||||
# symlink is to generate useful debuginfo packages
|
||||
rm -f %{name}-%{version}
|
||||
ln -sf . %{name}-%{version}
|
||||
%setup -T -D
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
python -m compileall appmenus-scripts
|
||||
python -O -m compileall appmenus-scripts
|
||||
export BACKEND_VMM=@BACKEND_VMM@
|
||||
(cd dom0-updates; make)
|
||||
(cd qrexec; make)
|
||||
(cd file-copy-vm; make)
|
||||
(cd doc; make manpages)
|
||||
|
||||
%install
|
||||
|
||||
### Appmenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules
|
||||
cp appmenus-scripts/qubes-core-appmenus.py $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.py
|
||||
cp appmenus-scripts/qubes-core-appmenus.pyc $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyc
|
||||
cp appmenus-scripts/qubes-core-appmenus.pyo $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyo
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus
|
||||
cp appmenus-scripts/*.sh $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
|
||||
cp appmenus-scripts/qubes-receive-appmenus $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
|
||||
|
||||
install -D appmenus-scripts/qvm-sync-appmenus $RPM_BUILD_ROOT/usr/bin/qvm-sync-appmenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/qubes-rpc/policy
|
||||
cp appmenus-scripts/qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes-rpc/
|
||||
cp appmenus-scripts/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
|
||||
cp -r appmenus-files/* $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
|
||||
## Appmenus
|
||||
install -d $RPM_BUILD_ROOT/etc/qubes-rpc/policy
|
||||
cp qubesappmenus/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
|
||||
### Dom0 updates
|
||||
install -D dom0-updates/qubes-dom0-updates.cron $RPM_BUILD_ROOT/etc/cron.daily/qubes-dom0-updates.cron
|
||||
install -D dom0-updates/qubes-dom0-update $RPM_BUILD_ROOT/usr/bin/qubes-dom0-update
|
||||
install -D dom0-updates/qubes-receive-updates $RPM_BUILD_ROOT/usr/libexec/qubes/qubes-receive-updates
|
||||
install -D dom0-updates/patch-dnf-yum-config $RPM_BUILD_ROOT/usr/lib/qubes/patch-dnf-yum-config
|
||||
install -m 0644 -D dom0-updates/qubes-cached.repo $RPM_BUILD_ROOT/etc/yum.real.repos.d/qubes-cached.repo
|
||||
install -D dom0-updates/qfile-dom0-unpacker $RPM_BUILD_ROOT/usr/libexec/qubes/qfile-dom0-unpacker
|
||||
install -m 0644 -D dom0-updates/qubes.ReceiveUpdates $RPM_BUILD_ROOT/etc/qubes-rpc/qubes.ReceiveUpdates
|
||||
@ -101,19 +96,14 @@ install -m 0664 -D dom0-updates/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/
|
||||
|
||||
install -d $RPM_BUILD_ROOT/var/lib/qubes/updates
|
||||
|
||||
# Qrexec
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp qrexec/qrexec-daemon $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp qrexec/qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
# XXX: Backward compatibility
|
||||
ln -s qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec_client
|
||||
cp qrexec/qrexec-policy $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp qrexec/qubes-rpc-multiplexer $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
# Qrexec services
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes/qubes-rpc $RPM_BUILD_ROOT/etc/qubes-rpc/policy
|
||||
cp qubes-rpc/* $RPM_BUILD_ROOT/usr/lib/qubes/qubes-rpc/
|
||||
for i in qubes-rpc/*; do ln -s ../../usr/lib/qubes/$i $RPM_BUILD_ROOT/etc/qubes-rpc/$(basename $i); done
|
||||
cp qubes-rpc-policy/* $RPM_BUILD_ROOT/etc/qubes-rpc/policy/
|
||||
|
||||
### pm-utils
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d
|
||||
cp pm-utils/01qubes-sync-vms-clock $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
cp pm-utils/51qubes-suspend-netvm $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
cp pm-utils/52qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/systemd/system
|
||||
cp pm-utils/qubes-suspend.service $RPM_BUILD_ROOT/usr/lib/systemd/system/
|
||||
@ -129,21 +119,37 @@ cp -r dracut/modules.d/* $RPM_BUILD_ROOT%{_dracutmoddir}/
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
|
||||
install -m 0644 -D system-config/limits-qubes.conf $RPM_BUILD_ROOT/etc/security/limits.d/99-qubes.conf
|
||||
install -D system-config/cpufreq-xen.modules $RPM_BUILD_ROOT/etc/sysconfig/modules/cpufreq-xen.modules
|
||||
cp system-config/iptables $RPM_BUILD_ROOT/etc/sysconfig
|
||||
cp system-config/ip6tables $RPM_BUILD_ROOT/etc/sysconfig
|
||||
install -m 0440 -D system-config/qubes.sudoers $RPM_BUILD_ROOT/etc/sudoers.d/qubes
|
||||
install -D system-config/polkit-1-qubes-allow-all.rules $RPM_BUILD_ROOT/etc/polkit-1/rules.d/00-qubes-allow-all.rules
|
||||
install -D system-config/qubes-dom0.modules $RPM_BUILD_ROOT/etc/sysconfig/modules/qubes-dom0.modules
|
||||
install -D system-config/qubes-sync-clock.cron $RPM_BUILD_ROOT/etc/cron.d/qubes-sync-clock.cron
|
||||
install -D system-config/lvm-cleanup.cron-daily $RPM_BUILD_ROOT/etc/cron.daily/lvm-cleanup
|
||||
install -d $RPM_BUILD_ROOT/etc/udev/rules.d
|
||||
install -m 644 system-config/00-qubes-ignore-devices.rules $RPM_BUILD_ROOT/etc/udev/rules.d/
|
||||
install -m 644 system-config/60-persistent-storage.rules $RPM_BUILD_ROOT/etc/udev/rules.d/
|
||||
install -m 644 system-config/12-qubes-ignore-lvm-devices.rules $RPM_BUILD_ROOT/etc/udev/rules.d/
|
||||
install -m 644 -D system-config/disable-lesspipe.sh $RPM_BUILD_ROOT/etc/profile.d/zz-disable-lesspipe.sh
|
||||
install -m 755 -D system-config/kernel-grub2.install $RPM_BUILD_ROOT/usr/lib/kernel/install.d/80-grub2.install
|
||||
install -m 755 -D system-config/kernel-xen-efi.install $RPM_BUILD_ROOT/usr/lib/kernel/install.d/90-xen-efi.install
|
||||
install -m 755 -D system-config/kernel-remove-bls.install $RPM_BUILD_ROOT/usr/lib/kernel/install.d/99-remove-bls.install
|
||||
install -m 644 -D system-config/75-qubes-dom0.preset \
|
||||
$RPM_BUILD_ROOT/usr/lib/systemd/system-preset/75-qubes-dom0.preset
|
||||
install -m 644 -D system-config/75-qubes-dom0-user.preset \
|
||||
$RPM_BUILD_ROOT/usr/lib/systemd/user-preset/75-qubes-dom0-user.preset
|
||||
install -m 644 -D system-config/99-qubes-default-disable.preset \
|
||||
$RPM_BUILD_ROOT/usr/lib/systemd/system-preset/99-qubes-default-disable.preset
|
||||
install -d $RPM_BUILD_ROOT/etc/dnf/protected.d
|
||||
install -m 0644 system-config/dnf-protected-qubes-core-dom0.conf \
|
||||
$RPM_BUILD_ROOT/etc/dnf/protected.d/qubes-core-dom0.conf
|
||||
|
||||
### Icons
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes/icons
|
||||
for icon in icons/*.png; do
|
||||
convert -resize 48 $icon $RPM_BUILD_ROOT/usr/share/qubes/$icon
|
||||
done
|
||||
|
||||
touch $RPM_BUILD_ROOT/var/lib/qubes/.qubes-exclude-block-devices
|
||||
|
||||
# file copy to VM
|
||||
install -m 755 file-copy-vm/qfile-dom0-agent $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
install -m 755 file-copy-vm/qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/
|
||||
install -m 755 file-copy-vm/qvm-copy $RPM_BUILD_ROOT/usr/bin/
|
||||
ln -s qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/qvm-move-to-vm
|
||||
ln -s qvm-copy $RPM_BUILD_ROOT/usr/bin/qvm-move
|
||||
|
||||
### Documentation
|
||||
(cd doc; make DESTDIR=$RPM_BUILD_ROOT install)
|
||||
@ -155,34 +161,22 @@ fi
|
||||
|
||||
%post
|
||||
|
||||
for i in /usr/share/qubes/icons/*.png ; do
|
||||
xdg-icon-resource install --noupdate --novendor --size 48 $i
|
||||
done
|
||||
xdg-icon-resource forceupdate
|
||||
|
||||
xdg-desktop-menu install /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-firefox.desktop
|
||||
|
||||
sed '/^reposdir\s*=/d' -i /etc/yum.conf
|
||||
echo reposdir=/etc/yum.real.repos.d >> /etc/yum.conf
|
||||
|
||||
sed '/^installonlypkgs\s*=/d' -i /etc/yum.conf
|
||||
echo 'installonlypkgs = kernel, kernel-qubes-vm' >> /etc/yum.conf
|
||||
|
||||
sed '/^distroverpkg\s*=/d' -i /etc/yum.conf
|
||||
echo 'distroverpkg = qubes-release' >> /etc/yum.conf
|
||||
/usr/lib/qubes/patch-dnf-yum-config
|
||||
|
||||
systemctl enable qubes-suspend.service >/dev/null 2>&1
|
||||
|
||||
# migrate dom0-updates check disable flag
|
||||
if [ $1 -ge 2 ]; then
|
||||
if [ -e /var/lib/qubes/updates/disable-updates ]; then
|
||||
qvm-features dom0 service.qubes-update-check ''
|
||||
rm -f /var/lib/qubes/updates/disable-updates
|
||||
fi
|
||||
fi
|
||||
|
||||
%preun
|
||||
if [ "$1" = 0 ] ; then
|
||||
# no more packages left
|
||||
|
||||
for i in /usr/share/qubes/icons/*.png ; do
|
||||
xdg-icon-resource uninstall --novendor --size 48 $i
|
||||
done
|
||||
|
||||
xdg-desktop-menu uninstall /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-firefox.desktop
|
||||
|
||||
systemctl disable qubes-suspend.service > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
@ -198,68 +192,64 @@ rm -f /lib/udev/rules.d/69-xorg-vmmouse.rules
|
||||
chmod -x /etc/grub.d/10_linux
|
||||
|
||||
%files
|
||||
%attr(2775,root,qubes) %dir /etc/qubes-rpc
|
||||
%attr(2775,root,qubes) %dir /etc/qubes-rpc/policy
|
||||
/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
/etc/qubes-rpc/qubes.SyncAppMenus
|
||||
%{python_sitearch}/qubes/modules/10appmenus.py
|
||||
%{python_sitearch}/qubes/modules/10appmenus.pyc
|
||||
%{python_sitearch}/qubes/modules/10appmenus.pyo
|
||||
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh
|
||||
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh
|
||||
/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh
|
||||
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
|
||||
/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh
|
||||
/usr/share/qubes-appmenus/qubes-appmenu-select.desktop
|
||||
/usr/share/qubes-appmenus/qubes-dispvm-firefox.desktop
|
||||
/usr/share/qubes-appmenus/qubes-dispvm.directory
|
||||
/usr/share/qubes-appmenus/qubes-servicevm.directory.template
|
||||
/usr/share/qubes-appmenus/qubes-start.desktop
|
||||
/usr/share/qubes-appmenus/qubes-templatevm.directory.template
|
||||
/usr/share/qubes-appmenus/qubes-vm.directory.template
|
||||
/usr/share/qubes-appmenus/hvm
|
||||
/usr/share/qubes/icons/*.png
|
||||
/usr/bin/qvm-sync-appmenus
|
||||
# Dom0 updates
|
||||
/etc/cron.daily/qubes-dom0-updates.cron
|
||||
/etc/yum.real.repos.d/qubes-cached.repo
|
||||
/usr/bin/qubes-dom0-update
|
||||
/usr/lib/qubes/patch-dnf-yum-config
|
||||
%attr(4750,root,qubes) /usr/libexec/qubes/qfile-dom0-unpacker
|
||||
/usr/libexec/qubes/qubes-receive-updates
|
||||
/etc/qubes-rpc/qubes.ReceiveUpdates
|
||||
%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.ReceiveUpdates
|
||||
%attr(0770,root,qubes) %dir /var/lib/qubes/updates
|
||||
# Qrexec services
|
||||
/etc/qubes-rpc/qubes.repos.*
|
||||
/usr/lib/qubes/qubes-rpc/qubes.repos.*
|
||||
%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.repos.List
|
||||
%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.repos.Enable
|
||||
%attr(0664,root,qubes) %config(noreplace) /etc/qubes-rpc/policy/qubes.repos.Disable
|
||||
# Dracut module
|
||||
/etc/dracut.conf.d/*
|
||||
%dir %{_dracutmoddir}/90macbook12-spi-driver
|
||||
%{_dracutmoddir}/90macbook12-spi-driver/*
|
||||
%dir %{_dracutmoddir}/90qubes-pciback
|
||||
%{_dracutmoddir}/90qubes-pciback/*
|
||||
%dir %{_dracutmoddir}/90extra-modules
|
||||
%{_dracutmoddir}/90extra-modules/*
|
||||
# Qrexec
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/qrexec-daemon
|
||||
/usr/lib/qubes/qrexec-client
|
||||
/usr/lib/qubes/qrexec_client
|
||||
/usr/lib/qubes/qubes-rpc-multiplexer
|
||||
/usr/lib/qubes/qrexec-policy
|
||||
# file copy
|
||||
/usr/bin/qvm-copy-to-vm
|
||||
/usr/bin/qvm-move-to-vm
|
||||
/usr/bin/qvm-copy
|
||||
/usr/bin/qvm-move
|
||||
/usr/lib/qubes/qfile-dom0-agent
|
||||
# pm-utils
|
||||
/usr/lib64/pm-utils/sleep.d/01qubes-sync-vms-clock
|
||||
/usr/lib64/pm-utils/sleep.d/51qubes-suspend-netvm
|
||||
/usr/lib64/pm-utils/sleep.d/52qubes-pause-vms
|
||||
/usr/lib/systemd/system/qubes-suspend.service
|
||||
# Others
|
||||
/etc/sysconfig/iptables
|
||||
/etc/sysconfig/ip6tables
|
||||
/etc/sysconfig/modules/qubes-dom0.modules
|
||||
/etc/sysconfig/modules/cpufreq-xen.modules
|
||||
/etc/sudoers.d/qubes
|
||||
/etc/polkit-1/rules.d/00-qubes-allow-all.rules
|
||||
/etc/security/limits.d/99-qubes.conf
|
||||
%config /etc/udev/rules.d/00-qubes-ignore-devices.rules
|
||||
%config(noreplace) /etc/udev/rules.d/60-persistent-storage.rules
|
||||
%config /etc/udev/rules.d/12-qubes-ignore-lvm-devices.rules
|
||||
%attr(0644,root,root) /etc/cron.d/qubes-sync-clock.cron
|
||||
/etc/cron.daily/lvm-cleanup
|
||||
%config(noreplace) /etc/profile.d/zz-disable-lesspipe.sh
|
||||
%config(noreplace) /etc/dnf/protected.d/qubes-core-dom0.conf
|
||||
/usr/lib/systemd/system-preset/75-qubes-dom0.preset
|
||||
/usr/lib/systemd/system-preset/99-qubes-default-disable.preset
|
||||
/usr/lib/systemd/user-preset/75-qubes-dom0-user.preset
|
||||
/var/lib/qubes/.qubes-exclude-block-devices
|
||||
# Man
|
||||
%{_mandir}/man1/qvm-*.1*
|
||||
%{_mandir}/man1/qubes-*.1*
|
||||
|
||||
%files kernel-install
|
||||
/usr/lib/kernel/install.d/80-grub2.install
|
||||
/usr/lib/kernel/install.d/90-xen-efi.install
|
||||
/usr/lib/kernel/install.d/99-remove-bls.install
|
||||
|
||||
%changelog
|
||||
@CHANGELOG@
|
@ -1,7 +1,5 @@
|
||||
%{!?version: %define version %(cat version_vaio_fixes)}
|
||||
|
||||
Name: qubes-core-dom0-vaio-fixes
|
||||
Version: %{version}
|
||||
Version: @VERSION1@
|
||||
Release: 1%{?dist}
|
||||
Summary: Additional scripts for supporting suspend on Vaio Z laptops
|
||||
Requires: alsa-utils
|
||||
@ -11,7 +9,7 @@ Vendor: Invisible Things Lab
|
||||
License: GPL
|
||||
URL: http://www.qubes-os.org
|
||||
|
||||
%define _builddir %(pwd)
|
||||
Source0: qubes-core-dom0-linux-@VERSION@.tar.gz
|
||||
|
||||
%description
|
||||
Additional scripts for supporting suspend on Vaio Z laptops.
|
||||
@ -19,14 +17,20 @@ Additional scripts for supporting suspend on Vaio Z laptops.
|
||||
Due to broken Linux GPU drivers we need to do some additional actions during
|
||||
suspend/resume.
|
||||
|
||||
%prep
|
||||
%setup -q -n qubes-core-dom0-linux-@VERSION@
|
||||
|
||||
%install
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d
|
||||
cp vaio-fixes/00sony-vaio-audio $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
cp vaio-fixes/99sony-vaio-audio $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
install -D vaio-fixes/00sony-vaio-audio $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
install -D vaio-fixes/99sony-vaio-audio $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/modprobe.d/
|
||||
cp vaio-fixes/snd-hda-intel-sony-vaio.conf $RPM_BUILD_ROOT/etc/modprobe.d/
|
||||
install -D vaio-fixes/snd-hda-intel-sony-vaio.conf $RPM_BUILD_ROOT/etc/modprobe.d/
|
||||
|
||||
%files
|
||||
/usr/lib64/pm-utils/sleep.d/00sony-vaio-audio
|
||||
/usr/lib64/pm-utils/sleep.d/99sony-vaio-audio
|
||||
/etc/modprobe.d/snd-hda-intel-sony-vaio.conf
|
||||
|
||||
%changelog
|
||||
@CHANGELOG@
|
@ -1,6 +1,11 @@
|
||||
# do not edit this file, it will be overwritten on update
|
||||
|
||||
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
|
||||
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*", ATTR{loop/backing_file}=="*/appvms/*/*.img*|*/vm-templates/*/*.img*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1", ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}="1"
|
||||
ACTION!="remove", SUBSYSTEM=="block", KERNEL=="xvd*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1", ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}="1"
|
||||
# ENV{DM_NAME} not available yet
|
||||
# Template VM disks
|
||||
ACTION!="remove", SUBSYSTEM=="block", ATTR{dm/name}=="snapshot-*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
|
||||
ACTION!="remove", SUBSYSTEM=="block", ATTR{dm/name}=="origin-*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
|
||||
# kpartx used for creating empty volatile.img, udevd tries to access the device
|
||||
# and prevent kpartx from removing them
|
||||
ACTION!="remove", SUBSYSTEM=="block", ATTR{dm/name}=="loop*p*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1"
|
||||
|
4
system-config/12-qubes-ignore-lvm-devices.rules
Normal file
@ -0,0 +1,4 @@
|
||||
# do not edit this file, it will be overwritten on update
|
||||
|
||||
# Skip VM images managed by lvm storage pool
|
||||
ACTION!="remove", SUBSYSTEM=="block", ENV{DM_LV_NAME}=="vm-*", ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}="1", ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}="1"
|
@ -1,87 +0,0 @@
|
||||
# do not edit this file, it will be overwritten on update
|
||||
|
||||
# persistent storage links: /dev/disk/{by-id,by-uuid,by-label,by-path}
|
||||
# scheme based on "Linux persistent device names", 2004, Hannes Reinecke <hare@suse.de>
|
||||
|
||||
# forward scsi device event to corresponding block device
|
||||
ACTION=="change", SUBSYSTEM=="scsi", ENV{DEVTYPE}=="scsi_device", TEST=="block", ATTR{block/*/uevent}="change"
|
||||
|
||||
ACTION=="remove", GOTO="persistent_storage_end"
|
||||
|
||||
# enable in-kernel media-presence polling
|
||||
ACTION=="add", SUBSYSTEM=="module", KERNEL=="block", ATTR{parameters/events_dfl_poll_msecs}=="0", ATTR{parameters/events_dfl_poll_msecs}="2000"
|
||||
|
||||
SUBSYSTEM!="block", GOTO="persistent_storage_end"
|
||||
|
||||
# skip rules for inappropriate block devices
|
||||
KERNEL=="loop*|fd*|mtd*|nbd*|gnbd*|btibm*|dm-*|md*|zram*", GOTO="persistent_storage_end"
|
||||
|
||||
# ignore partitions that span the entire disk
|
||||
TEST=="whole_disk", GOTO="persistent_storage_end"
|
||||
|
||||
# for partitions import parent information
|
||||
ENV{DEVTYPE}=="partition", IMPORT{parent}="ID_*"
|
||||
|
||||
# virtio-blk
|
||||
KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}"
|
||||
KERNEL=="vd*[0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}-part%n"
|
||||
|
||||
# ATA devices using the "scsi" subsystem
|
||||
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{vendor}=="ATA", IMPORT{program}="ata_id --export $devnode"
|
||||
# ATA/ATAPI devices (SPC-3 or later) using the "scsi" subsystem
|
||||
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", ATTRS{type}=="5", ATTRS{scsi_level}=="[6-9]*", IMPORT{program}="ata_id --export $devnode"
|
||||
|
||||
# Run ata_id on non-removable USB Mass Storage (SATA/PATA disks in enclosures)
|
||||
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", ATTR{removable}=="0", SUBSYSTEMS=="usb", IMPORT{program}="ata_id --export $devnode"
|
||||
# Otherwise, fall back to using usb_id for USB devices
|
||||
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id"
|
||||
|
||||
# scsi devices
|
||||
KERNEL=="sd*[!0-9]|sr*", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="scsi"
|
||||
KERNEL=="cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --export --whitelisted -d $devnode", ENV{ID_BUS}="cciss"
|
||||
KERNEL=="sd*|sr*|cciss*", ENV{DEVTYPE}=="disk", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}"
|
||||
KERNEL=="sd*|cciss*", ENV{DEVTYPE}=="partition", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/$env{ID_BUS}-$env{ID_SERIAL}-part%n"
|
||||
|
||||
# firewire
|
||||
KERNEL=="sd*[!0-9]|sr*", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}"
|
||||
KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$attr{ieee1394_id}-part%n"
|
||||
|
||||
KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}"
|
||||
KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
|
||||
KERNEL=="mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}"
|
||||
KERNEL=="mspblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/memstick-$env{ID_NAME}_$env{ID_SERIAL}-part%n"
|
||||
|
||||
# by-path (parent device path)
|
||||
ENV{DEVTYPE}=="disk", DEVPATH!="*/virtual/*", IMPORT{builtin}="path_id"
|
||||
ENV{DEVTYPE}=="disk", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_PATH}=="?*", SYMLINK+="disk/by-path/$env{ID_PATH}-part%n"
|
||||
|
||||
# skip unpartitioned removable media devices from drivers which do not send "change" events
|
||||
ENV{DEVTYPE}=="disk", KERNEL!="sd*|sr*", ATTR{removable}=="1", GOTO="persistent_storage_end"
|
||||
|
||||
# probe filesystem metadata of optical drives which have a media inserted
|
||||
KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="?*", \
|
||||
IMPORT{builtin}="blkid --offset=$env{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}"
|
||||
# single-session CDs do not have ID_CDROM_MEDIA_SESSION_LAST_OFFSET
|
||||
KERNEL=="sr*", ENV{DISK_EJECT_REQUEST}!="?*", ENV{ID_CDROM_MEDIA_TRACK_COUNT_DATA}=="?*", ENV{ID_CDROM_MEDIA_SESSION_LAST_OFFSET}=="", \
|
||||
IMPORT{builtin}="blkid --noraid"
|
||||
|
||||
# probe filesystem metadata of disks
|
||||
KERNEL!="sr*", IMPORT{builtin}="blkid"
|
||||
|
||||
# watch metadata changes by tools closing the device after writing
|
||||
KERNEL!="sr*", OPTIONS+="watch"
|
||||
|
||||
# by-label/by-uuid links (filesystem metadata)
|
||||
ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
|
||||
ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
|
||||
|
||||
# by-id (World Wide Name)
|
||||
ENV{DEVTYPE}=="disk", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}"
|
||||
ENV{DEVTYPE}=="partition", ENV{ID_WWN_WITH_EXTENSION}=="?*", SYMLINK+="disk/by-id/wwn-$env{ID_WWN_WITH_EXTENSION}-part%n"
|
||||
|
||||
# by-partlabel/by-partuuid links (partition metadata)
|
||||
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
|
||||
ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
|
||||
|
||||
LABEL="persistent_storage_end"
|
2
system-config/75-qubes-dom0-user.preset
Normal file
@ -0,0 +1,2 @@
|
||||
enable dbus.socket
|
||||
enable dbus-daemon.service
|
55
system-config/75-qubes-dom0.preset
Normal file
@ -0,0 +1,55 @@
|
||||
enable gdm.service
|
||||
enable lightdm.service
|
||||
enable slim.service
|
||||
enable lxdm.service
|
||||
enable sddm.service
|
||||
enable kdm.service
|
||||
enable xdm.service
|
||||
|
||||
|
||||
disable systemd-timesyncd.service
|
||||
disable systemd-networkd.service
|
||||
disable systemd-resolved.service
|
||||
|
||||
# Locally-running services
|
||||
enable lvm2-monitor.*
|
||||
enable lvm2-lvmetad.*
|
||||
enable dm-event.*
|
||||
enable dmraid-activation.service
|
||||
enable fstrim.timer
|
||||
|
||||
enable dbus.socket
|
||||
enable dbus-daemon.service
|
||||
|
||||
enable abrtd.service
|
||||
enable abrt-ccpp.service
|
||||
enable abrt-oops.service
|
||||
enable abrt-xorg.service
|
||||
enable abrt-vmcore.service
|
||||
|
||||
enable xenstored.service
|
||||
enable xenstored.socket
|
||||
enable xenstored_ro.socket
|
||||
enable xenconsoled.service
|
||||
enable xen-init-dom0.service
|
||||
enable libvirtd.service
|
||||
enable virlockd.socket
|
||||
|
||||
|
||||
enable upower.service
|
||||
enable crond.service
|
||||
|
||||
|
||||
# Qubes services
|
||||
enable qubes-core.service
|
||||
enable qubes-netvm.service
|
||||
enable qubes-meminfo-writer-dom0.service
|
||||
enable qubes-db-dom0.service
|
||||
enable qubes-qmemman.service
|
||||
enable qubes-suspend.service
|
||||
enable qubes-setupdvm.service
|
||||
enable qubes-qrexec-policy-daemon.service
|
||||
enable qubesd.service
|
||||
enable anti-evil-maid-unseal.service
|
||||
enable anti-evil-maid-check-mount-devs.service
|
||||
enable anti-evil-maid-seal.service
|
1
system-config/99-qubes-default-disable.preset
Normal file
@ -0,0 +1 @@
|
||||
disable *
|
@ -7,6 +7,7 @@ if modinfo cpufreq-xen > /dev/null 2>&1; then
|
||||
for f in /sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_governor; do
|
||||
echo xen > $f
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
if modinfo xen-acpi-processor &>/dev/null; then
|
||||
modprobe xen-acpi-processor
|
||||
fi
|
||||
|
1
system-config/disable-lesspipe.sh
Normal file
@ -0,0 +1 @@
|
||||
unset LESSOPEN LESSCLOSE
|
1
system-config/dnf-protected-qubes-core-dom0.conf
Normal file
@ -0,0 +1 @@
|
||||
qubes-core-dom0
|
@ -1,8 +0,0 @@
|
||||
# Generated by ip6tables-save v1.4.14 on Tue Sep 25 16:00:20 2012
|
||||
*filter
|
||||
:INPUT DROP [1:72]
|
||||
:FORWARD DROP [0:0]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
-A INPUT -i lo -j ACCEPT
|
||||
COMMIT
|
||||
# Completed on Tue Sep 25 16:00:20 2012
|
@ -1,30 +0,0 @@
|
||||
# Generated by iptables-save v1.4.5 on Mon Sep 6 08:57:46 2010
|
||||
*nat
|
||||
:PREROUTING ACCEPT [85:5912]
|
||||
:OUTPUT ACCEPT [0:0]
|
||||
:POSTROUTING ACCEPT [0:0]
|
||||
:PR-QBS - [0:0]
|
||||
:PR-QBS-SERVICES - [0:0]
|
||||
-A PREROUTING -j PR-QBS
|
||||
-A PREROUTING -j PR-QBS-SERVICES
|
||||
-A POSTROUTING -o vif+ -j ACCEPT
|
||||
-A POSTROUTING -o lo -j ACCEPT
|
||||
-A POSTROUTING -j MASQUERADE
|
||||
COMMIT
|
||||
# Completed on Mon Sep 6 08:57:46 2010
|
||||
# Generated by iptables-save v1.4.5 on Mon Sep 6 08:57:46 2010
|
||||
*filter
|
||||
:INPUT ACCEPT [168:11399]
|
||||
:FORWARD ACCEPT [0:0]
|
||||
:OUTPUT ACCEPT [128:12536]
|
||||
-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP
|
||||
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
-A INPUT -p icmp -j ACCEPT
|
||||
-A INPUT -i lo -j ACCEPT
|
||||
-A INPUT -j REJECT --reject-with icmp-host-prohibited
|
||||
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
||||
-A FORWARD -i vif+ -o vif+ -j DROP
|
||||
-A FORWARD -i vif+ -j ACCEPT
|
||||
-A FORWARD -j DROP
|
||||
COMMIT
|
||||
# Completed on Mon Sep 6 08:57:46 2010
|
28
system-config/kernel-grub2.install
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
COMMAND="$1"
|
||||
KVER="$2"
|
||||
BOOT_DIR_ABS="$3"
|
||||
|
||||
case "$COMMAND" in
|
||||
add)
|
||||
# use newer image if available
|
||||
if [ -e "$BOOT_DIR_ABS"/initrd ]; then
|
||||
cp -u "$BOOT_DIR_ABS"/initrd "/boot/initramfs-${KVER}.img"
|
||||
fi
|
||||
if [ ! -e "/boot/initramfs-${KVER}.img" ]; then
|
||||
dracut "/boot/initramfs-${KVER}.img" "$KVER"
|
||||
fi
|
||||
;;
|
||||
remove)
|
||||
rm -f "/boot/initramfs-${KVER}.img"
|
||||
;;
|
||||
esac
|
||||
if [ -x /usr/sbin/grub2-mkconfig ]; then
|
||||
if [ -e /boot/grub2/grub.cfg ]; then
|
||||
grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
fi
|
||||
if [ -e /boot/efi/EFI/qubes/grub.cfg ]; then
|
||||
grub2-mkconfig -o /boot/efi/EFI/qubes/grub.cfg
|
||||
fi
|
||||
fi
|
21
system-config/kernel-remove-bls.install
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
# The Boot Loader Specification is not useful for Qubes OS, because it handles
|
||||
# only direct Linux boot, not Xen or any other multiboot application (like
|
||||
# tboot).
|
||||
# Because of that Qubes OS still uses generated grub2 configuration.
|
||||
# Unfortunately the sole existence of /boot/${MACHINE_ID} changes behaviour of
|
||||
# some tools - for example default output file in dracut. So forcibly remove
|
||||
# the directory (which was just created...).
|
||||
|
||||
[[ -f /etc/machine-id ]] && read MACHINE_ID < /etc/machine-id
|
||||
|
||||
if [[ $MACHINE_ID ]] && ( [[ -d /boot/${MACHINE_ID} ]] || [[ -L /boot/${MACHINE_ID} ]] ); then
|
||||
rm -rf /boot/${MACHINE_ID}
|
||||
fi
|
||||
if [[ $MACHINE_ID ]] && ( [[ -d /boot/efi/${MACHINE_ID} ]] || [[ -L /boot/efi/${MACHINE_ID} ]] ); then
|
||||
rm -rf /boot/efi/${MACHINE_ID}
|
||||
rm -f /boot/efi/loader/entries/${MACHINE_ID}-*.conf
|
||||
# remove only when empty
|
||||
rmdir /boot/efi/loader/entries /boot/efi/loader || :
|
||||
fi
|
110
system-config/kernel-xen-efi.install
Executable file
@ -0,0 +1,110 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
COMMAND="$1"
|
||||
KVER="$2"
|
||||
|
||||
ESP_MOUNTPOINT=/boot/efi
|
||||
|
||||
EFI_DIR=$(efibootmgr -v 2>/dev/null | awk '
|
||||
/^BootCurrent:/ { current=$2; }
|
||||
/^Boot....\* .*xen\.efi/ {
|
||||
if ("Boot" current "*" == $1) {
|
||||
sub(".*File\\(", "");
|
||||
sub("\\\\xen.efi\\).*", "");
|
||||
gsub("\\\\", "/");
|
||||
print;
|
||||
}
|
||||
}')
|
||||
|
||||
if [ -z "$EFI_DIR" ]; then
|
||||
EFI_DIR="$ESP_MOUNTPOINT/EFI/qubes"
|
||||
else
|
||||
EFI_DIR="$ESP_MOUNTPOINT$EFI_DIR"
|
||||
fi
|
||||
|
||||
if [ ! -r "$EFI_DIR/xen.cfg" ]; then
|
||||
# non-EFI system
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
case "$COMMAND" in
|
||||
add)
|
||||
if ! fgrep -q "[${KVER}]" $EFI_DIR/xen.cfg; then
|
||||
# take the default section and use it as a template for the new entry
|
||||
awk -F = --assign "kver=${KVER}" '
|
||||
/^\[/ {
|
||||
# section header - previous section (if any) ended
|
||||
|
||||
# if default section already processed, that is all
|
||||
if (in_default) exit;
|
||||
in_global=0;
|
||||
in_default=0;
|
||||
}
|
||||
/\[global\]/ {
|
||||
in_global=1;
|
||||
}
|
||||
/^\[/ {
|
||||
if ("[" default_name "]" == $0) {
|
||||
in_default=1;
|
||||
print "[" kver "]";
|
||||
next;
|
||||
}
|
||||
}
|
||||
/^default=/ {
|
||||
if (in_global)
|
||||
default_name=$2;
|
||||
}
|
||||
/^kernel=/ {
|
||||
if (in_default) {
|
||||
sub("=[^ ]*", "=vmlinuz-" kver);
|
||||
}
|
||||
}
|
||||
/^ramdisk=/ {
|
||||
if (in_default) {
|
||||
sub("=[^ ]*", "=initramfs-" kver ".img");
|
||||
}
|
||||
}
|
||||
{
|
||||
if (in_default) {
|
||||
print;
|
||||
}
|
||||
}' $EFI_DIR/xen.cfg >> $EFI_DIR/xen.cfg
|
||||
|
||||
# then change the default
|
||||
sed -e "s/default=.*/default=$KVER/" -i $EFI_DIR/xen.cfg
|
||||
fi
|
||||
|
||||
cp "/boot/vmlinuz-$KVER" "$EFI_DIR/"
|
||||
if [ -e "/boot/initramfs-${KVER}.img" ]; then
|
||||
cp -f "/boot/initramfs-${KVER}.img" "$EFI_DIR/"
|
||||
else
|
||||
dracut -f "$EFI_DIR/initramfs-${KVER}.img" "$KVER"
|
||||
fi
|
||||
;;
|
||||
remove)
|
||||
# don't care about changing default= line - yum should prevent removing
|
||||
# currently running kernel
|
||||
if [ -r $EFI_DIR/xen.cfg ]; then
|
||||
awk -F = --assign "kver=${KVER}" '
|
||||
/^\[/ {
|
||||
# section header - previous section (if any) ended
|
||||
|
||||
in_current=0;
|
||||
}
|
||||
/^\[/ {
|
||||
if ($0 == "[" kver "]")
|
||||
in_current=1;
|
||||
}
|
||||
{
|
||||
if (!in_current) {
|
||||
print;
|
||||
}
|
||||
}' $EFI_DIR/xen.cfg > $EFI_DIR/xen.cfg.new
|
||||
mv $EFI_DIR/xen.cfg.new $EFI_DIR/xen.cfg
|
||||
fi
|
||||
rm -f "$EFI_DIR/initramfs-${KVER}.img"
|
||||
rm -f "$EFI_DIR/vmlinuz-${KVER}"
|
||||
;;
|
||||
esac
|
3
system-config/lvm-cleanup.cron-daily
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
find /etc/lvm/archive/ -type f -mtime +1 -name '*.vg' -delete
|
@ -1 +1 @@
|
||||
*/6 * * * * root /usr/bin/qvm-sync-clock > /dev/null 2>&1 || true
|
||||
0 */1 * * * root /usr/bin/qvm-sync-clock > /dev/null 2>&1 || true
|
||||
|
@ -1 +0,0 @@
|
||||
1.6.1
|