]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/sbin/ifupdown2d
ifupdown2 2.0.0 release
[mirror_ifupdown2.git] / ifupdown2 / sbin / ifupdown2d
1 #!/usr/bin/python
2 #
3 # Copyright 2017 Cumulus Networks, Inc. All rights reserved.
4 # Authors:
5 # Roopa Prabhu, roopa@cumulusnetworks.com
6 # Julien Fortin, julien@cumulusnetworks.com
7 #
8 # ifupdown2 --
9 # tool to configure network interfaces
10 #
11
12 import os
13 import re
14 import json
15 import fcntl
16 import struct
17 import signal
18 import socket
19 import daemon
20 import select
21 import datetime
22 import threading
23
24 try:
25 import ifupdown2.ifupdown.argv
26
27 from ifupdown2.ifupdown.log import log
28 from ifupdown2.ifupdown.main import Ifupdown2
29 except ImportError:
30 import ifupdown.argv
31
32 from ifupdown.log import log
33 from ifupdown.main import Ifupdown2
34
35
36 class Daemon:
37 shutdown_event = threading.Event()
38
39 def __init__(self):
40 self.uds = None
41 self.context = None
42 self.working_directory = '/var/run/ifupdown2d/'
43 self.server_address = '/var/run/ifupdown2d/uds'
44
45 if not os.path.exists(self.working_directory):
46 log.info('creating %s' % self.working_directory)
47 os.makedirs(self.working_directory, mode=0755)
48
49 if os.path.exists(self.server_address):
50 log.info('removing uds %s' % self.server_address)
51 os.remove(self.server_address)
52
53 self.context = daemon.DaemonContext(
54 working_directory=self.working_directory,
55 signal_map={
56 signal.SIGINT: self.signal_handler,
57 signal.SIGTERM: self.signal_handler,
58 signal.SIGQUIT: self.signal_handler,
59 },
60 umask=0o22
61 )
62
63 try:
64 self.SO_PEERCRED = socket.SO_PEERCRED
65 except AttributeError:
66 # powerpc is the only non-generic we care about. alpha, mips,
67 # sparc, and parisc also have non-generic values.
68 machine = os.uname()[4]
69 if re.search(r'^(ppc|powerpc)', machine):
70 self.SO_PASSCRED = 20
71 self.SO_PEERCRED = 21
72 else:
73 self.SO_PASSCRED = 16
74 self.SO_PEERCRED = 17
75
76 log.info('daemonizing ifupdown2d...')
77 self.context.open()
78
79 log.info('preloading all necessary modules')
80 self.preload_imports()
81
82 try:
83 log.info('opening UNIX socket')
84 self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
85 fcntl.fcntl(self.uds.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
86 except Exception as e:
87 raise Exception('socket: %s' % str(e))
88 try:
89 self.uds.bind(self.server_address)
90 except Exception as e:
91 raise Exception('bind: %s' % str(e))
92 try:
93 self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
94 except Exception as e:
95 raise Exception('setsockopt: %s' % str(e))
96 try:
97 self.uds.listen(1)
98 except Exception as e:
99 raise Exception('listen: %s' % str(e))
100 os.chmod(self.server_address, 0777)
101
102 def __del__(self):
103 if self.context:
104 self.context.close()
105 if self.uds:
106 self.uds.close()
107
108 @staticmethod
109 def preload_imports():
110 """
111 preloading all the necessary modules
112 at first will increase performances
113 """
114 try:
115 import io
116 import pdb
117 import imp
118 import sets
119 import json
120 import glob
121 import time
122 import copy
123 import errno
124 import pprint
125 import atexit
126 import ipaddr
127 import cPickle
128 import logging
129 import argparse
130 import StringIO
131 import datetime
132 import traceback
133 import itertools
134 import subprocess
135 import argcomplete
136 import collections
137 import ConfigParser
138 import pkg_resources
139
140 import ifupdown2.ifupdown.exceptions
141 import ifupdown2.ifupdown.graph
142 import ifupdown2.ifupdown.iface
143 import ifupdown2.ifupdown.iff
144 import ifupdown2.ifupdown.ifupdownbase
145 import ifupdown2.ifupdown.ifupdownbase
146 import ifupdown2.ifupdown.ifupdownconfig
147 import ifupdown2.ifupdown.ifupdownflags
148 import ifupdown2.ifupdown.ifupdownmain
149 import ifupdown2.ifupdown.netlink
150 import ifupdown2.ifupdown.networkinterfaces
151 import ifupdown2.ifupdown.policymanager
152 import ifupdown2.ifupdown.scheduler
153 import ifupdown2.ifupdown.statemanager
154 import ifupdown2.ifupdown.template
155 import ifupdown2.ifupdown.utils
156
157 import ifupdown2.ifupdownaddons.cache
158 import ifupdown2.ifupdownaddons.dhclient
159 import ifupdown2.ifupdownaddons.mstpctlutil
160 import ifupdown2.ifupdownaddons.LinkUtils
161 import ifupdown2.ifupdownaddons.modulebase
162 import ifupdown2.ifupdownaddons.systemutils
163 import ifupdown2.ifupdownaddons.utilsbase
164 except ImportError, e:
165 raise ImportError('%s - required module not found' % str(e))
166
167 @staticmethod
168 def signal_handler(sig, frame):
169 log.info('received %s' % 'SIGINT' if sig == signal.SIGINT else 'SIGTERM')
170 Daemon.shutdown_event.set()
171
172 @staticmethod
173 def user_waiting_for_reply():
174 return not log.is_syslog()
175
176 def run(self):
177 try:
178 while True:
179 if Daemon.shutdown_event.is_set():
180 log.info("shutdown signal RXed, breaking out loop")
181 break
182
183 try:
184 (client_socket, client_address) = self.uds.accept()
185 except socket.error as e:
186 log.error(str(e))
187 break
188
189 pid = os.fork()
190 if pid == 0:
191 exit(self.ifupdown2(client_socket))
192 else:
193 log.tx_data(json.dumps({'pid': pid}), socket=client_socket)
194
195 start = datetime.datetime.now()
196 status = os.WEXITSTATUS(os.waitpid(pid, 0)[1])
197 end = datetime.datetime.now()
198
199 log.tx_data(json.dumps({'status': status}), socket=client_socket)
200 client_socket.close()
201
202 log.info('exit status %d - in %ssecs'
203 % (status, (end - start).total_seconds()))
204
205 except Exception as e:
206 log.error(e)
207 self.uds.close()
208
209 def get_client_uid(self, client_socket):
210 creds = client_socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i'))
211 (pid, uid, gid) = struct.unpack('3i', creds)
212 log.debug('client uid %d' % uid)
213 return uid
214
215 @staticmethod
216 def get_client_request(client_socket):
217 """
218 This function handles requests of any length.
219
220 if the received json is longer than 65k it will be truncated
221 several calls to recv will be needed, we store the data until
222 we can decode them with the json library.
223 """
224 data = []
225 while True:
226 log.debug('waiting for request on client socket')
227 ready = select.select([client_socket], [], [])
228
229 if ready and ready[0] and ready[0][0] == client_socket:
230 # data available start reading
231 raw_data = client_socket.recv(65536)
232
233 try:
234 return json.loads(raw_data)
235 except ValueError:
236 # the json is incomplete
237 data.append(raw_data)
238
239 if len(data) > 1:
240 try:
241 return json.loads(''.join(data))
242 except ValueError:
243 pass
244
245 def ifupdown2(self, client_socket):
246 try:
247 fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
248
249 ifupdown2 = Ifupdown2(daemon=True, uid=self.get_client_uid(client_socket))
250 ifupdown2.set_signal_handlers()
251
252 request = self.get_client_request(client_socket)
253 log.info('request: %s' % request['argv'])
254
255 ifupdown2.parse_argv(request['argv'])
256 # adjust the logger with argv
257 ifupdown2.update_logger(socket=client_socket)
258
259 try:
260 status = ifupdown2.main(request['stdin'])
261 except Exception as e:
262 log.error(str(e))
263 status = 1
264
265 except ifupdown2.ifupdown.argv.ArgvParseError as e:
266 log.update_current_logger(syslog=False, verbose=True, debug=False)
267 log.set_socket(client_socket)
268 e.log_error()
269 status = 1
270 except Exception as e:
271 log.error(e)
272 status = 1
273
274 log.flush()
275 log.set_socket(None)
276 client_socket.close()
277 return status
278
279
280 if __name__ == '__main__':
281 try:
282 Daemon().run()
283 except Exception as e:
284 print e
285 log.error(str(e))
286 import traceback
287 log.error(traceback.format_exc())
288 exit(1)