Merge remote-tracking branch 'flapflap/de-network_configuration'
[tails-test.git] / config / chroot_local-includes / usr / local / sbin / tails-additional-software
blob3b7614db1d4f0e2a479e817a652b7d92d395a72b
1 #!/usr/bin/env python
3 import gettext
4 import os.path
5 import pwd
6 import subprocess
7 import sys
8 import syslog
10 PERSISTENCE_DIR = "/live/persistence/TailsData_unlocked"
11 PACKAGES_LIST_FILE = PERSISTENCE_DIR + "/live-additional-software.conf"
12 ACTIVATION_FILE = "/var/run/live-additional-software/activated"
14 def _launch_apt_get(specific_args):
15 """Launch apt-get with given args
17 Launch apt-get with given arguments list, log its standard and error output
18 and return its returncode"""
19 apt_get_env = os.environ.copy()
20 # The environnment provided in GDM PostLogin hooks doesn't contain /sbin/
21 # which is required by dpkg. Let's use the default path for root in Tails.
22 apt_get_env['PATH'] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
23 # We will log the output and want it in English when included in bug reports
24 apt_get_env['LANG'] = "C"
25 args = ["apt-get", "--quiet", "--yes"]
26 args.extend(specific_args)
27 apt_get = subprocess.Popen(
28 args,
29 env=apt_get_env,
30 stderr=subprocess.STDOUT,
31 stdout=subprocess.PIPE)
32 for line in iter(apt_get.stdout.readline, ''):
33 if not line.startswith('('):
34 syslog.syslog(line.rstrip())
35 apt_get.wait()
36 if apt_get.returncode:
37 syslog.syslog(syslog.LOG_WARNING,
38 "apt-get exited with returncode %i" % apt_get.returncode)
39 return apt_get.returncode
41 def _notify(title, body):
42 """Display a notification to the user of the live system
43 """
44 cmd = "/usr/local/sbin/tails-notify-user"
45 try:
46 # XXX: replace with check_output when Tails will be based on Wheezy
47 # (which includes Python 2.7)
48 notify_user = subprocess.Popen([cmd, title, body],
49 stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
50 notify_user_output = notify_user.stdout.read()
51 notify_user.wait()
52 if notify_user.returncode != 0:
53 syslog.syslog(syslog.LOG_WARNING, "Warning: unable to notify the user. %s returned with exit code %s"
54 % (cmd, notify_user.returncode))
55 syslog.syslog(syslog.LOG_WARNING, "%s output follows: %s."
56 % (cmd, notify_user_output))
57 syslog.syslog(syslog.LOG_WARNING, "The notification was: %s %s" % (title, body))
58 except OSError, e:
59 syslog.syslog(syslog.LOG_WARNING, "Warning: unable to notify the user. %s" % e)
60 syslog.syslog(syslog.LOG_WARNING, "The notification was: %s %s" % (title, body))
62 def has_additional_packages_list():
63 """Return true iff PACKAGES_LIST_FILE exists
64 """
65 return os.path.isfile(PACKAGES_LIST_FILE)
67 def get_additional_packages():
68 """Returns the list of all the additional packages
69 """
70 packages = []
71 if has_additional_packages_list():
72 with open(PACKAGES_LIST_FILE) as f:
73 for line in f:
74 line = line.strip()
75 if line: packages.append(line)
76 f.closed
77 return packages
79 def install_additional_packages():
80 """The subcommand which activates and installs all additional packages
81 """
82 syslog.syslog("Starting to install additional software...")
83 if has_additional_packages_list():
84 syslog.syslog("Found additional packages list")
85 elif os.path.isdir(PERSISTENCE_DIR):
86 syslog.syslog(syslog.LOG_WARNING, "Warning: no configuration file found, creating an empty one.")
87 create_additional_packages_list()
88 return True
89 else:
90 syslog.syslog(syslog.LOG_WARNING, "Warning: persistence is not mounted, exiting")
91 return True
92 packages = get_additional_packages()
93 if not packages:
94 syslog.syslog(syslog.LOG_WARNING, "Warning: no packages to install, exiting")
95 return True
96 set_activated()
97 syslog.syslog("Will install the following packages: %s" % " ".join(packages))
98 apt_get_returncode = _launch_apt_get(["--no-remove",
99 "--option", "DPkg::Options::=--force-confold",
100 "install"] + packages)
101 if apt_get_returncode:
102 syslog.syslog(syslog.LOG_WARNING, "Warning: installation of %s failed" % " ".join(packages))
103 return False
104 else:
105 syslog.syslog("Installation completed successfully.")
106 return True
108 def upgrade_additional_packages():
109 """The subcommand which upgrades all additional packages if they are activated
111 if not is_activated():
112 syslog.syslog(syslog.LOG_WARNING, "Warning: additional packages not activated, exiting")
113 return True
114 syslog.syslog("Starting to upgrade additional software...")
115 apt_get_returncode = _launch_apt_get(["update"])
116 if apt_get_returncode:
117 syslog.syslog(syslog.LOG_WARNING, "Warning: the update failed.")
118 _notify(_("Your additional software"),
119 _("The upgrade failed. This might be due to a network problem. \
120 Please check your network connection, try to restart Tails, or read the system \
121 log to understand better the problem."))
122 return False
123 if install_additional_packages():
124 _notify(_("Your additional software"),
125 _("The upgrade was successful."))
126 return True
127 else:
128 _notify(_("Your additional software"),
129 _("The upgrade failed. This might be due to a network problem. \
130 Please check your network connection, try to restart Tails, or read the system \
131 log to understand better the problem."))
132 return False
134 def create_additional_packages_list():
135 """Creates the additional packages list
137 Creates the additional packages list file with the right permissions.
138 The caller must ensure the file doesn't already exist.
140 assert not has_additional_packages_list(), "%s already exists" % PACKAGES_LIST_FILE
141 syslog.syslog("Creating additional software configuration file")
142 f = open(PACKAGES_LIST_FILE, 'w')
143 f.closed
144 os.chmod(PACKAGES_LIST_FILE, 0600)
145 os.chown(PACKAGES_LIST_FILE,
146 pwd.getpwnam('tails-persistence-setup').pw_uid,
147 pwd.getpwnam('tails-persistence-setup').pw_gid)
149 def is_activated():
150 """Check if additional software has been activated
152 return os.path.isfile(ACTIVATION_FILE)
154 def set_activated():
155 """Save that additional software has been activated
157 syslog.syslog("Activating persistent software packages")
158 activation_file_dir = os.path.dirname(ACTIVATION_FILE)
159 if not os.path.exists(activation_file_dir):
160 os.makedirs(activation_file_dir)
161 try:
162 f = open(ACTIVATION_FILE, 'w')
163 finally:
164 if f: f.close()
166 def print_help():
167 """The subcommand which displays help
169 sys.stderr.write("Usage: %s <subcommand>\n" % program_name)
170 sys.stderr.write("""Subcommands:
171 install: activate and install additional software
172 upgrade: upgrade additional software if activated\n""")
174 if __name__ == "__main__":
175 program_name = os.path.basename(sys.argv[0])
177 syslog.openlog("%s[%i]" % (program_name, os.getpid()))
178 gettext.install("tails")
180 if len(sys.argv) < 2:
181 print_help()
182 sys.exit(4)
184 if sys.argv[1] == "install":
185 if not install_additional_packages():
186 sys.exit(1)
187 elif sys.argv[1] == "upgrade":
188 if not upgrade_additional_packages():
189 sys.exit(2)
190 else:
191 print_help()
192 sys.exit(4)