]>
Commit | Line | Data |
---|---|---|
d486dd0d JF |
1 | #!/usr/bin/python |
2 | # | |
3 | # Copyright 2016 Cumulus Networks, Inc. All rights reserved. | |
4 | # Author: Julien Fortin, julien@cumulusnetworks.com | |
5 | # | |
6 | # | |
7 | ||
8 | import os | |
9 | import re | |
10 | import sys | |
11 | import json | |
12 | import errno | |
13 | import struct | |
14 | import select | |
15 | import socket | |
16 | import signal | |
17 | ||
18 | try: | |
19 | import ifupdown2.ifupdown.config as core_config | |
20 | from ifupdown2.ifupdown.log import log | |
21 | from ifupdown2 import __version__ | |
22 | ||
23 | core_config.__version__ = __version__ | |
24 | except: | |
25 | import ifupdown.config as core_config | |
26 | from ifupdown.log import log | |
27 | ||
28 | core_config.__version__ = __import__('__init__').__version__ | |
29 | ||
30 | ||
31 | class Ifupdown2Complete(Exception): | |
32 | def __init__(self, status): | |
33 | self.status = status | |
34 | ||
35 | ||
36 | class Ifupdown2Client: | |
37 | def __init__(self, argv): | |
38 | ||
39 | self.stdin = None | |
40 | self.argv = argv | |
41 | self.data = '' | |
42 | self.HEADER_SIZE = 4 | |
43 | self.daemon_pid = -1 | |
44 | ||
45 | self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | |
46 | try: | |
47 | self.socket.connect('/var/run/ifupdown2d/uds') | |
48 | ||
49 | signal.signal(signal.SIGINT, self.signal_handler) | |
50 | signal.signal(signal.SIGTERM, self.signal_handler) | |
51 | signal.signal(signal.SIGQUIT, self.signal_handler) | |
52 | ||
53 | try: | |
54 | self.SO_PEERCRED = socket.SO_PEERCRED | |
55 | except AttributeError: | |
56 | # powerpc is the only non-generic we care about. alpha, mips, | |
57 | # sparc, and parisc also have non-generic values. | |
58 | machine = os.uname()[4] | |
59 | if re.search(r'^(ppc|powerpc)', machine): | |
60 | self.SO_PASSCRED = 20 | |
61 | self.SO_PEERCRED = 21 | |
62 | else: | |
63 | self.SO_PASSCRED = 16 | |
64 | self.SO_PEERCRED = 17 | |
65 | try: | |
66 | self.socket.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1) | |
67 | except Exception as e: | |
68 | raise Exception('setsockopt: %s' % str(e)) | |
69 | ||
70 | except socket.error: | |
71 | self.socket.close() | |
72 | self.socket = None | |
73 | sys.stderr.write(""" | |
74 | ERROR: %s could not connect to ifupdown2d | |
75 | ||
76 | Try starting ifupdown2d with: | |
77 | sudo systemctl start ifupdown2d | |
78 | ||
79 | To configure ifupdown2d to start when the box boots: | |
80 | sudo systemctl enable ifupdown2d | |
81 | """ % argv[0]) | |
82 | ||
83 | def __del__(self): | |
84 | if self.socket: | |
85 | self.socket.close() | |
86 | ||
87 | def signal_handler(self, sig, frame): | |
88 | if self.daemon_pid > 0: | |
89 | os.kill(self.daemon_pid, sig) | |
90 | ||
91 | def read_data(self): | |
92 | ready = select.select([self.socket], [], []) | |
93 | if ready and ready[0] and ready[0][0] == self.socket: | |
94 | d = self.socket.recv(65536) | |
95 | if self.data: | |
96 | self.data += d | |
97 | else: | |
98 | self.data = d | |
99 | return True | |
100 | return False | |
101 | ||
102 | def get_packets(self): | |
103 | """ | |
104 | ifupdown2 output is divided into "json packets" | |
105 | the first 4 bytes is the size of the next json | |
106 | object to etract | |
107 | ||
108 | """ | |
109 | data_size = len(self.data) | |
110 | if not data_size: | |
111 | raise Ifupdown2Complete(status=1) | |
112 | ||
113 | packets = [] | |
114 | try: | |
115 | while data_size > 0: | |
116 | packet_len = struct.unpack('=I', self.data[:self.HEADER_SIZE])[0] | |
117 | packet_data = self.data[self.HEADER_SIZE:packet_len + self.HEADER_SIZE] | |
118 | ||
119 | fmt = "=%ds" % packet_len | |
120 | ||
121 | packets.append(json.loads(struct.unpack(fmt, packet_data)[0])) | |
122 | ||
123 | self.data = self.data[self.HEADER_SIZE + packet_len:] | |
124 | data_size -= self.HEADER_SIZE + packet_len | |
125 | except: | |
126 | pass | |
127 | return packets | |
128 | ||
129 | def process_packets(self, packets): | |
130 | for packet in packets: | |
131 | if 'pid' in packet: | |
132 | self.daemon_pid = packet['pid'] | |
133 | if 'stdout' in packet: | |
134 | sys.stdout.write(packet['stdout']) | |
135 | if 'stderr' in packet: | |
136 | sys.stderr.write(packet['stderr']) | |
137 | if 'status' in packet: | |
138 | raise Ifupdown2Complete(packet['status']) | |
139 | ||
140 | def run(self): | |
141 | status = 1 | |
142 | if self.socket: | |
143 | for arg in ['-i', '--interfaces']: | |
144 | try: | |
145 | if self.argv[self.argv.index(arg) + 1] == '-': | |
146 | self.stdin = sys.stdin.read() | |
147 | continue | |
148 | except (ValueError, IndexError): | |
149 | pass | |
150 | ||
151 | self.socket.send(json.dumps({ | |
152 | 'argv': self.argv, | |
153 | 'stdin': self.stdin | |
154 | })) | |
155 | ||
156 | try: | |
157 | while True: | |
158 | try: | |
159 | self.read_data() | |
160 | self.process_packets(self.get_packets()) | |
161 | except Ifupdown2Complete as e: | |
162 | status = e.status | |
163 | break | |
164 | except Exception as e: | |
165 | if ((isinstance(e.args, tuple) and e[0] == 4) | |
166 | or (hasattr(e, 'errno') and e.errno == errno.EINTR)): | |
167 | pass | |
168 | else: | |
169 | raise | |
170 | except Exception as e: | |
171 | sys.stderr.write(str(e)) | |
172 | finally: | |
173 | self.socket.close() | |
174 | return status if status != None else 1 | |
175 | ||
176 | ||
177 | def ifupdown2_standalone(): | |
178 | try: | |
179 | import ifupdown2.ifupdown.main as main_ifupdown2 | |
180 | except: | |
181 | import ifupdown.main as main_ifupdown2 | |
182 | ifupdown2 = main_ifupdown2.Ifupdown2(daemon=False, uid=os.geteuid()) | |
183 | ifupdown2.parse_argv(sys.argv) | |
184 | ifupdown2.update_logger() | |
185 | return ifupdown2.main() | |
186 | ||
187 | ||
188 | def main(): | |
189 | try: | |
190 | if 'use_daemon=yes' in open(core_config.IFUPDOWN2_CONF_PATH).read(): | |
191 | return Ifupdown2Client(sys.argv).run() | |
192 | else: | |
193 | return ifupdown2_standalone() | |
194 | except KeyboardInterrupt: | |
195 | return 1 | |
196 | except Exception as e: | |
197 | log.error(str(e)) | |
198 | return 1 | |
199 | ||
200 | ||
201 | if __name__ == '__main__': | |
202 | try: | |
203 | sys.exit(main()) | |
204 | except KeyboardInterrupt: | |
205 | sys.exit(1) |