elog/mod_save_summary: wrap IOError, bug #441948
[portage.git] / pym / portage / elog / mod_save_summary.py
1 # elog/mod_save_summary.py - elog dispatch module
2 # Copyright 2006-2012 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import errno
6 import io
7 import time
8 import portage
9 from portage import os
10 from portage import _encodings
11 from portage import _unicode_decode
12 from portage import _unicode_encode
13 from portage.data import portage_gid, portage_uid
14 from portage.localization import _
15 from portage.package.ebuild.prepare_build_dirs import _ensure_log_subdirs
16 from portage.util import apply_permissions, ensure_dirs, normalize_path
17
18 def process(mysettings, key, logentries, fulltext):
19         if mysettings.get("PORT_LOGDIR"):
20                 logdir = normalize_path(mysettings["PORT_LOGDIR"])
21         else:
22                 logdir = os.path.join(os.sep, mysettings["EPREFIX"].lstrip(os.sep),
23                         "var", "log", "portage")
24
25         if not os.path.isdir(logdir):
26                 # Only initialize group/mode if the directory doesn't
27                 # exist, so that we don't override permissions if they
28                 # were previously set by the administrator.
29                 # NOTE: These permissions should be compatible with our
30                 # default logrotate config as discussed in bug 374287.
31                 logdir_uid = -1
32                 if portage.data.secpass >= 2:
33                         logdir_uid = portage_uid
34                 ensure_dirs(logdir, uid=logdir_uid, gid=portage_gid, mode=0o2770)
35
36         elogdir = os.path.join(logdir, "elog")
37         _ensure_log_subdirs(logdir, elogdir)
38
39         # TODO: Locking
40         elogfilename = elogdir+"/summary.log"
41         try:
42                 elogfile = io.open(_unicode_encode(elogfilename,
43                         encoding=_encodings['fs'], errors='strict'),
44                         mode='a', encoding=_encodings['content'],
45                         errors='backslashreplace')
46         except IOError as e:
47                 func_call = "open('%s', 'a')" % elogfilename
48                 if e.errno == errno.EACCES:
49                         raise portage.exception.PermissionDenied(func_call)
50                 elif e.errno == errno.EPERM:
51                         raise portage.exception.OperationNotPermitted(func_call)
52                 elif e.errno == errno.EROFS:
53                         raise portage.exception.ReadOnlyFileSystem(func_call)
54                 else:
55                         raise
56
57         # Copy group permission bits from parent directory.
58         elogdir_st = os.stat(elogdir)
59         elogdir_gid = elogdir_st.st_gid
60         elogdir_grp_mode = 0o060 & elogdir_st.st_mode
61
62         # Copy the uid from the parent directory if we have privileges
63         # to do so, for compatibility with our default logrotate
64         # config (see bug 378451). With the "su portage portage"
65         # directive and logrotate-3.8.0, logrotate's chown call during
66         # the compression phase will only succeed if the log file's uid
67         # is portage_uid.
68         logfile_uid = -1
69         if portage.data.secpass >= 2:
70                 logfile_uid = elogdir_st.st_uid
71         apply_permissions(elogfilename, uid=logfile_uid, gid=elogdir_gid,
72                 mode=elogdir_grp_mode, mask=0)
73
74         time_str = time.strftime("%Y-%m-%d %H:%M:%S %Z",
75                 time.localtime(time.time()))
76         # Avoid potential UnicodeDecodeError later.
77         time_str = _unicode_decode(time_str,
78                 encoding=_encodings['content'], errors='replace')
79         elogfile.write(_unicode_decode(
80                 _(">>> Messages generated by process " +
81                 "%(pid)d on %(time)s for package %(pkg)s:\n\n") %
82                 {"pid": os.getpid(), "time": time_str, "pkg": key}))
83         elogfile.write(_unicode_decode(fulltext))
84         elogfile.write(_unicode_decode("\n"))
85         elogfile.close()
86
87         return elogfilename