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(
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())
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
44 cmd
= "/usr/local/sbin/tails-notify-user"
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()
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
))
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
65 return os
.path
.isfile(PACKAGES_LIST_FILE
)
67 def get_additional_packages():
68 """Returns the list of all the additional packages
71 if has_additional_packages_list():
72 with
open(PACKAGES_LIST_FILE
) as f
:
75 if line
: packages
.append(line
)
79 def install_additional_packages():
80 """The subcommand which activates and installs all additional packages
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()
90 syslog
.syslog(syslog
.LOG_WARNING
, "Warning: persistence is not mounted, exiting")
92 packages
= get_additional_packages()
94 syslog
.syslog(syslog
.LOG_WARNING
, "Warning: no packages to install, exiting")
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
))
105 syslog
.syslog("Installation completed successfully.")
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")
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."))
123 if install_additional_packages():
124 _notify(_("Your additional software"),
125 _("The upgrade was successful."))
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."))
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')
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
)
150 """Check if additional software has been activated
152 return os
.path
.isfile(ACTIVATION_FILE
)
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
)
162 f
= open(ACTIVATION_FILE
, 'w')
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:
184 if sys
.argv
[1] == "install":
185 if not install_additional_packages():
187 elif sys
.argv
[1] == "upgrade":
188 if not upgrade_additional_packages():