Fixes to rpmlint and license related issues
[platform/upstream/lsb.git] / lsbinstall
1 #!/usr/bin/python
2
3 import sys
4 import os
5 import shutil
6 import socket
7 import commands
8 from tempfile import NamedTemporaryFile
9 from initdutils import load_lsbinstall_info, save_lsbinstall_info
10
11 # These keep getting revised... *sigh*
12 objecttypes = (
13     #'init',
14     'profile',
15     'service',
16     'inet', # XXX - reenable when implemented
17     #'crontab',
18     #'man'
19     )
20
21 def installed_message(objectname):
22     print objectname, 'is installed'
23
24 def handle_generic_install(options, args, location, showinstloc=False):
25     filename = args[0]
26     basename = os.path.basename(filename)
27     instloc = os.path.join(location, basename)
28     package = args.package
29
30     filemap = load_lsbinstall_info()
31     fileinfo = filemap.get((package, filename))
32     
33     if options.check:
34         if fileinfo and os.path.exists(fileinfo):
35             installed_message(fileinfo)
36             return
37         else:
38             sys.exit(1)
39     elif options.remove:
40         if os.path.exists(fileinfo):
41             try:
42                 os.unlink(fileinfo)
43             except (IOError, OSError, os.error), why:
44                 print >> sys.stderr, 'Removal of %s failed: %s' % (
45                     instloc, str(why))
46                 sys.exit(1)
47
48         # Remove it from the database, even if it was previously removed
49         del filemap[(package, filename)]
50         save_lsbinstall_info(filemap)
51         return
52
53     if os.path.exists(instloc) and options.package:
54         instloc = os.path.join(location, '%s.%s' % (options.package, basename))
55
56     if os.path.exists(instloc):
57         print >> sys.stderr, 'Unable to install %s: %s exists' % (
58             filename, instloc)
59         sys.exit(1)
60
61     if not os.path.exists(location):
62         try:
63             os.makedirs(location)
64         except (IOError, OSError, os.error), why:
65             print >> sys.stderr, 'Unable to create %s to install %s: %s' % (
66                 location, filename, str(why))
67             sys.exit(1)
68         
69     try:
70         shutil.copy2(filename, instloc)
71     except (IOError, os.error), why:
72         print >> sys.stderr, 'Installation of %s as %s failed: %s' % (
73             filename, instloc, str(why))
74         sys.exit(1)
75         
76     if showinstloc:
77         print instloc
78
79     filemap[(package, filename)] = instloc
80     save_lsbinstall_info(filemap)
81
82 def handle_service(options, args):
83     # LSB says we don't actually have to remove these things...
84     if options.remove:
85         sys.exit(0)
86
87     pkg = options.package or '<unknown package>'
88
89     try:
90         pproto = args[0]
91         port, proto = pproto.split('/', 1)
92         port = int(port)
93     except:
94         print >> sys.stderr, 'You must specify a port/protocol pair as the first argument.'
95         sys.exit(2)
96
97     if options.check:
98         try:
99             serv = socket.getservbyport(port, proto)
100         except socket.error:
101             sys.exit(1)
102
103         print '%d/%s corresponds to service %s' % (port, proto, serv)
104         return
105
106     sname = args[1]
107     saliases = args[2:]
108
109     try:
110         serv = socket.getservbyport(port, proto)
111     except socket.error:
112         serv = None
113
114     if serv:
115         # Editing existing service
116         fpin = open('/etc/services')
117         fpout = NamedTemporaryFile('w', prefix='services-', dir='/etc')
118         newfname = fpout.name
119
120         for line in fpin:
121             line = line.rstrip()
122             if not line.startswith('#'):
123                 lcomment = ''
124                 if '#' in line:
125                     line, lcomment = line.split('#', 1)
126                 
127                 bits = line.split(None, 2)
128                 lname, lpproto = bits[:2]
129                 laliases = []
130                 if len(bits) > 2:
131                     laliases = bits[2].split()
132
133                 lport, lproto = lpproto.split('/')
134                 lport = int(lport)
135
136                 if lport == port and lproto == proto:
137                     # We've found the right line
138                     if name != lname:
139                         aliases += [name]
140
141                     for a in aliases:
142                         if a != lname and a not in laliases:
143                             laliases += [a]
144                 elif name == lname or name in laliases:
145                     # name shows up, but in wrong protocol/port
146                     print >> sys.stderr, 'Conflict between specified addition and /etc/services; aborting.'
147                     fpout.close()
148                     os.unlink(newfname)
149                     sys.exit(1)
150
151                 endbits = ''
152                 if laliases:
153                     endbits = ' '.join(laliases) + ' '
154                 if lcomment:
155                     endbits += '#'+lcomment
156                     
157                 line = '%15s %15s %s' % lname, lpproto, endbits
158                 line = line.rstrip()
159
160             print >> fpout, line
161         
162         fpin.close()
163         fpout.close()
164         # Make sure /etc/services is always available
165         shutil.copy2('/etc/services', '/etc/services~')
166         os.rename(newfname, '/etc/services')
167         return
168
169     fp = open('/etc/services', 'a')
170     print >> fp, '%15s %15s %s # Added by lsbinstall for %s' % (
171         sname, pproto, ' '.join(saliases), pkg)
172     fp.close()
173
174 def handle_inet(options, args, parser):
175     cmd = 'update-inetd --group LSB '
176
177     alist = list(args[0].split(':'))
178     if len(alist) < 2:
179         parser.error("The operand must include a service and protocol.")
180         return
181     
182     if not alist[1]:
183         alist[1] = 'tcp'
184     
185     if options.remove:
186         parts = r'%s\s+.*\s+%s\s+.*' % (re.escape(alist[0], alist[1]))
187         cmd += '--remove '+commands.mkarg(parts)
188     elif options.check:
189         return
190     else:
191         if len(alist) != 6:
192             parser.error('The operand must have six colon-separated arguments.')
193             return
194         newalist = [alist[0], alist[2], alist[1]] + alist[3:]
195         cmd += '--add '+commands.mkarg('\t'.join(newalist))
196
197     os.system(cmd)
198     pass
199
200 def handle_man(options, args):
201     # Try to figure out the man page section
202     section = 1
203     
204     location = '/usr/local/share/man/man%d/' % section
205
206     handle_generic_install(options, args, location)
207
208
209 def main():
210     from optparse import OptionParser
211
212     parser = OptionParser('usage: %prog [-r|-c] -t TYPE arguments...')
213     parser.add_option('-c', '--check', dest="check", default=False,
214                       action="store_true", help='check whether or not an '
215                       'object of this type is already installed')
216     parser.add_option('-r', '--remove', action="store_true",
217                       dest="remove", default=False,
218                       help='remove the named object')
219     parser.add_option('-t', '--type', dest="type", type='choice',
220                       default=None, choices=objecttypes,
221                       help='type of object to be '
222                       'installed: one of %s' % ', '.join(objecttypes) )
223     parser.add_option('-p', '--package', dest="package", default=None,
224                       help='LSB package to operate on')
225
226     (options, args) = parser.parse_args()
227
228     if len(args) < 1:
229         parser.error('You must specify at least one argument.')
230     elif (options.remove or options.check) and len(args) > 1:
231         parser.error('You may only specify one argument with --check or '
232                      '--remove.')
233
234     if not options.type:
235         parser.error('You must specify an object type.')
236
237     if not options.package and options.type not in ['service']:
238         parser.error('You must specify a package with %s objects.' %
239                      options.type)
240
241     if options.type == 'init':
242         if len(args) > 1:
243             parser.error('Only one argument supported for %s' % options.type)
244         handle_generic_install(options, args, '/etc/init.d', showinstloc=True)
245     elif options.type == 'profile':
246         if len(args) > 1:
247             parser.error('Only one argument supported for %s' % options.type)
248         # profile.d does nothing on Debian... sigh
249         handle_generic_install(options, args, '/etc/profile.d')
250     elif options.type == 'service':
251         if len(args) < 2 and not (options.remove or options.check):
252             parser.error('You must specify at least two arguments when adding a service entry.')
253         elif len(args) != 1 and (options.remove or options.check):
254             parser.error('You must specify one argument when removing or checking a service entry.')
255         handle_service(options, args)
256     elif options.type == 'inet':
257         handle_inet(options, args, parser)
258     elif options.type == 'crontab':
259         if len(args) > 1:
260             parser.error('Only one argument supported for %s' % options.type)
261         handle_generic_install(options, args, '/etc/cron.d')
262     elif options.type == 'man':
263         if len(args) > 1:
264             parser.error('Only one argument supported for %s' % options.type)
265         handle_man(options, args)
266     else:
267         print >> sys.stderr, 'Unsupported type %s' % options.type
268         sys.exit(1)
269
270 if __name__ == '__main__':
271     main()