2c6dfbe96fd6637eef5ddcd85e13f40ce642d1de
[portage.git] / bin / install.py
1 #!/usr/bin/python
2 # Copyright 2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import os
6 import stat
7 import sys
8 import subprocess
9 import traceback
10
11 import portage
12 from portage.util._argparse import ArgumentParser
13 from portage.util.movefile import _copyxattr
14 from portage.exception import OperationNotSupported
15
16 # Change back to original cwd _after_ all imports (bug #469338).
17 os.chdir(os.environ["__PORTAGE_HELPER_CWD"])
18
19 def parse_args(args):
20         """
21         Parse the command line arguments using optparse for python 2.6 compatibility
22         Args:
23           args: a list of the white space delimited command line
24         Returns:
25           tuple of the Namespace of parsed options, and a list of order parameters
26         """
27         parser = ArgumentParser(add_help=False)
28
29         parser.add_argument(
30                 "-b",
31                 action="store_true",
32                 dest="shortopt_b"
33         )
34         parser.add_argument(
35                 "--backup",
36                 action="store",
37                 dest="backup"
38                 )
39         parser.add_argument(
40                 "-c",
41                 action="store_true",
42                 dest="shortopt_c"
43         )
44         parser.add_argument(
45                 "--compare",
46                 "-C",
47                 action="store_true",
48                 dest="compare"
49         )
50         parser.add_argument(
51                 "--directory",
52                         "-d",
53                 action="store_true",
54                 dest="directory"
55         )
56         parser.add_argument(
57                 "-D",
58                 action="store_true",
59                 dest="shortopt_D"
60         )
61         parser.add_argument(
62                 "--owner",
63                 "-o",
64                 action="store", 
65                 dest="owner"
66         )
67         parser.add_argument(
68                 "--group",
69                 "-g",
70                 action="store",
71                 dest="group"
72         )
73         parser.add_argument(
74                 "--mode",
75                 "-m",
76                 action="store",
77                 dest="mode"
78         )
79         parser.add_argument(
80                 "--preserve-timestamps",
81                 "-p",
82                 action="store_true",
83                 dest="preserve_timestamps"
84         )
85         parser.add_argument(
86                 "--strip",
87                 "-s",
88                 action="store_true",
89                 dest="strip"
90         )
91         parser.add_argument(
92                 "--strip-program",
93                 action="store",
94                 dest="strip_program"
95         )
96         parser.add_argument(
97                 "--suffix",
98                 "-S",
99                 action="store",
100                 dest="suffix"
101         )
102         parser.add_argument(
103                 "--target-directory",
104                 "-t",
105                 action="store",
106                 dest="target_directory"
107         )
108         parser.add_argument(
109                 "--no-target-directory",
110                 "-T",
111                 action="store_true",
112                 dest="no_target_directory"
113         )
114         parser.add_argument(
115                 "--context",
116                 "-Z",
117                 action="store",
118                 dest="context"
119         )
120         parser.add_argument(
121                 "--verbose",
122                 "-v",
123                 action="store_true",
124                 dest="verbose"
125         )
126         parser.add_argument(
127                 "--help",
128                 action="store_true",
129                 dest="help"
130         )
131         parser.add_argument(
132                 "--version",
133                 action="store_true",
134                 dest="version"
135         )
136
137         # Use parse_known_args for maximum compatibility with
138         # getopt handling of non-option file arguments. Note
139         # that parser.add_argument("files", nargs='+') would
140         # be subtly incompatible because it requires that all
141         # of the file arguments be grouped sequentially. Also
142         # note that we have to explicitly call add_argument
143         # for known options in order for argparse to correctly
144         # separate option arguments from file arguments in all
145         # cases (it also allows for optparse compatibility).
146         parsed_args = parser.parse_known_args()
147
148         opts  = parsed_args[0]
149         files = parsed_args[1]
150         files = [f for f in files if f != "--"] # filter out "--"
151
152         return (opts, files)
153
154
155 def copy_xattrs(opts, files):
156         """
157         Copy the extended attributes using portage.util.movefile._copyxattr
158         Args:
159           opts:  Namespace of the parsed command line otions
160           files: list of ordered command line parameters which should be files/directories
161         Returns:
162           system exit code
163         """
164         if opts.directory or not files:
165                 return os.EX_OK
166
167         if opts.target_directory is None:
168                 source, target = files[:-1], files[-1]
169                 target_is_directory = os.path.isdir(target)
170         else:
171                 source, target = files, opts.target_directory
172                 target_is_directory = True
173
174         exclude = os.environ.get("PORTAGE_XATTR_EXCLUDE", "security.* system.nfs4_acl")
175
176         try:
177                 if target_is_directory:
178                         for s in source:
179                                 abs_path = os.path.join(target, os.path.basename(s))
180                                 _copyxattr(s, abs_path, exclude=exclude)
181                 else:
182                         _copyxattr(source[0], target, exclude=exclude)
183                 return os.EX_OK
184
185         except OperationNotSupported:
186                 traceback.print_exc()
187                 return os.EX_OSERR
188
189
190 def Which(filename, path=None, exclude=None):
191         """
192         Find the absolute path of 'filename' in a given search 'path'
193         Args:
194           filename: basename of the file
195           path: colon delimited search path
196           exclude: path of file to exclude
197         """
198         if path is None:
199                 path = os.environ.get('PATH', '')
200
201         if exclude is not None:
202                 st = os.stat(exclude)
203                 exclude = (st.st_ino, st.st_dev)
204
205         for p in path.split(':'):
206                 p = os.path.join(p, filename)
207                 if os.access(p, os.X_OK):
208                         try:
209                                 st = os.stat(p)
210                         except OSError:
211                                 # file disappeared?
212                                 pass
213                         else:
214                                 if stat.S_ISREG(st.st_mode) and \
215                                         (exclude is None or exclude != (st.st_ino, st.st_dev)):
216                                         return p
217
218         return None
219
220
221 def main(args):
222         opts, files = parse_args(args)
223         install_binary = Which('install', exclude=os.environ["__PORTAGE_HELPER_PATH"])
224         if install_binary is None:
225                 sys.stderr.write("install: command not found\n")
226                 return 127
227
228         cmdline = [install_binary]
229         cmdline += args
230
231         if sys.hexversion >= 0x3000000:
232                 # We can't trust that the filesystem encoding (locale dependent)
233                 # correctly matches the arguments, so use surrogateescape to
234                 # pass through the original argv bytes for Python 3.
235                 fs_encoding = sys.getfilesystemencoding()
236                 cmdline = [x.encode(fs_encoding, 'surrogateescape') for x in cmdline]
237                 files = [x.encode(fs_encoding, 'surrogateescape') for x in files]
238                 if opts.target_directory is not None:
239                         opts.target_directory = \
240                                 opts.target_directory.encode(fs_encoding, 'surrogateescape')
241
242         returncode = subprocess.call(cmdline)
243         if returncode == os.EX_OK:
244                 returncode = copy_xattrs(opts, files)
245                 if returncode != os.EX_OK:
246                         portage.util.writemsg("!!! install: copy_xattrs failed with the "
247                                 "following arguments: %s\n" %
248                                 " ".join(portage._shell_quote(x) for x in args), noiselevel=-1)
249         return returncode
250
251
252 if __name__ == "__main__":
253         sys.exit(main(sys.argv[1:]))