2019-12-17 01:04:54 +01:00
|
|
|
# Copyright (C) 2017, 2018, 2019 Cumulus Networks, Inc. all rights reserved
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License as
|
|
|
|
# published by the Free Software Foundation; version 2.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
# General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
# 02110-1301, USA.
|
|
|
|
#
|
|
|
|
# https://www.gnu.org/licenses/gpl-2.0-standalone.html
|
|
|
|
#
|
|
|
|
# Author:
|
|
|
|
# Julien Fortin, julien@cumulusnetworks.com
|
|
|
|
#
|
|
|
|
# io -- all io (file) handlers
|
|
|
|
#
|
|
|
|
|
|
|
|
import json
|
|
|
|
import struct
|
|
|
|
import socket
|
|
|
|
import select
|
|
|
|
|
|
|
|
try:
|
|
|
|
from ifupdown2.lib.base_objects import BaseObject
|
|
|
|
except ImportError:
|
|
|
|
from lib.base_objects import BaseObject
|
|
|
|
|
|
|
|
|
|
|
|
class IO(BaseObject):
|
|
|
|
def __init__(self):
|
|
|
|
BaseObject.__init__(self)
|
|
|
|
|
|
|
|
def write_to_file(self, path, string):
|
|
|
|
try:
|
|
|
|
self.logger.info("writing \"%s\" to file %s" % (string, path))
|
|
|
|
with open(path, "w") as f:
|
|
|
|
f.write(string)
|
|
|
|
return True
|
2019-12-17 16:55:49 +01:00
|
|
|
except IOError as e:
|
2019-12-09 22:31:46 +01:00
|
|
|
self.logger.warning("error while writing to file %s: %s" % (path, str(e)))
|
2019-12-17 01:04:54 +01:00
|
|
|
return False
|
|
|
|
|
|
|
|
def write_to_file_dry_run(self, path, string):
|
|
|
|
self.log_info_dry_run("writing \"%s\" to file %s" % (string, path))
|
|
|
|
return True
|
|
|
|
|
|
|
|
def read_file_oneline(self, path):
|
|
|
|
try:
|
|
|
|
self.logger.info("reading '%s'" % path)
|
|
|
|
with open(path, "r") as f:
|
|
|
|
return f.readline().strip("\n")
|
|
|
|
except:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def read_file_oneline_dry_run(self, path):
|
|
|
|
self.log_info_dry_run("reading \"%s\"" % path)
|
|
|
|
return None
|
|
|
|
|
|
|
|
def read_file(self, path):
|
|
|
|
""" read file and return lines from the file """
|
|
|
|
try:
|
|
|
|
self.logger.info("reading '%s'" % path)
|
|
|
|
with open(path, "r") as f:
|
|
|
|
return f.readlines()
|
|
|
|
except:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
class SocketIO(object):
|
|
|
|
"""
|
|
|
|
Helper class to provide common TX/RX methods for socket
|
|
|
|
communication to both client and daemon.
|
|
|
|
"""
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def tx_data(_socket, data):
|
|
|
|
"""
|
|
|
|
We don't send raw data over the socket, we pack it with the length
|
|
|
|
(first 4 bytes) then with the data. That way the the transfer is more
|
|
|
|
reliable
|
|
|
|
"""
|
|
|
|
ready = select.select([], [_socket], [])
|
|
|
|
if ready and ready[1] and ready[1][0] == _socket:
|
|
|
|
frmt = "=%ds" % len(data)
|
|
|
|
packed_msg = struct.pack(frmt, data)
|
|
|
|
packed_hdr = struct.pack("=I", len(packed_msg))
|
|
|
|
_socket.sendall(packed_hdr + packed_msg)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def rx_json_packet(_socket):
|
|
|
|
"""
|
|
|
|
Reading data from socket. Unpacking the packets sent by "tx_data"
|
|
|
|
first 4 bytes are the length of the following data. The data should
|
|
|
|
be in json format
|
|
|
|
"""
|
|
|
|
ready = select.select([_socket], [], [])
|
|
|
|
|
|
|
|
if ready and ready[0] and ready[0][0] == _socket:
|
|
|
|
|
|
|
|
header_data = _socket.recv(4)
|
|
|
|
|
|
|
|
if not header_data:
|
|
|
|
raise Exception("rx_json_packet: socket closed")
|
|
|
|
if len(header_data) < 4:
|
|
|
|
raise Exception("rx_json_packet: invalid data received")
|
|
|
|
|
|
|
|
data_len = struct.unpack("=I", header_data)[0]
|
|
|
|
data = _socket.recv(data_len)
|
|
|
|
|
|
|
|
while len(data) < data_len:
|
|
|
|
data = data + _socket.recv(data_len - len(data))
|
|
|
|
|
|
|
|
return json.loads(data)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_socket_peer_cred(self, _socket):
|
|
|
|
"""
|
|
|
|
Returns tuple of (pid, uid, gid) of connected AF_UNIX stream socket
|
|
|
|
:param _socket:
|
|
|
|
:return:
|
|
|
|
"""
|
|
|
|
return struct.unpack("3i", _socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize("3i")))
|