kopia lustrzana https://github.com/weetmuts/wmbusmeters
Exit and print info when background rtl_wmbus process fails to start.
rodzic
7aae7aa6c2
commit
43f0d9b051
18
CHANGES
18
CHANGES
|
@ -1,7 +1,21 @@
|
|||
|
||||
Verison 0.9.3: 2019-03-20
|
||||
Version 0.9.4: 2019-04-03
|
||||
|
||||
Added initial support for the Tauron Amiplus electricity meter type (amiplus).
|
||||
The device auto can now detect an rtlsdr dongle and start
|
||||
rtl_sdr|rtl_wmbus properly. It can only detecht the rtlsdr
|
||||
dongle if the new udev rule has been installed, which will
|
||||
create the symlink /dev/rtlsdr when the dongle is inserted.
|
||||
|
||||
Added the meter vendor Echelon to the generic amiplus meter type.
|
||||
(The Echelon meter seems to be a standard electricity meter with a
|
||||
wmbus addon sourced from Develco.)
|
||||
|
||||
Version 0.9.3: 2019-03-20
|
||||
|
||||
Added initial support for the generic Tauron Amiplus electricity meter type (amiplus).
|
||||
This is actually a generic meter type, that will match the meter vendors
|
||||
that provide meters under the Amiplus brand to Taurn. The first vendor to
|
||||
be supported is from Apator.
|
||||
|
||||
Added support for the at-wmbus-16-2 snap on meter (apator162).
|
||||
Unfortunately it uses a vendor specific protocol,
|
||||
|
|
2
Makefile
2
Makefile
|
@ -20,7 +20,7 @@
|
|||
# make DEBUG=true
|
||||
# make DEBUG=true HOST=arm
|
||||
|
||||
VERSION=0.9.2
|
||||
VERSION=0.9.4
|
||||
|
||||
ifeq "$(HOST)" "arm"
|
||||
CXX=arm-linux-gnueabihf-g++
|
||||
|
|
42
README.md
42
README.md
|
@ -17,7 +17,7 @@ The program runs on GNU/Linux, MacOSX and Raspberry Pi.
|
|||
|
||||
# Run as a daemon
|
||||
|
||||
Remove the wmbus dongle (im871a or amb8465) from your computer.
|
||||
Remove the wmbus dongle (im871a,amb8465) or the generic rtlsdr dongle (RTL2838) from your computer.
|
||||
|
||||
`make; sudo make install` will install wmbusmeters as a daemon that starts
|
||||
automatically when an appropriate wmbus usb dongle is inserted in the computer.
|
||||
|
@ -29,9 +29,11 @@ Check the config file /etc/wmbusmeters.conf:
|
|||
loglevel=normal
|
||||
device=auto
|
||||
logtelegrams=false
|
||||
format=json
|
||||
meterfiles=/var/log/wmbusmeters/meter_readings
|
||||
meterfilesaction=overwrite
|
||||
logfile=/var/log/wmbusmeters/wmbusmeters.log
|
||||
shell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters -m "$METER_JSON"
|
||||
shell=/usr/bin/mosquitto_pub -h localhost -t wmbusmeters/$METER_ID -m "$METER_JSON"
|
||||
```
|
||||
|
||||
Then add a meter file in /etc/wmbusmeters.d/MyTapWater
|
||||
|
@ -44,6 +46,8 @@ key=00112233445566778899AABBCCDDEEFF
|
|||
|
||||
Now plugin your wmbus dongle. Wmbusmeters should start automatically,
|
||||
check with `tail -f /var/log/syslog` and `tail -f /var/log/wmbusmeters/wmbusmeters.log`
|
||||
(If you are using an rtlsdr dongle, then make sure the binaries /usr/bin/rtl_sdr and
|
||||
/usr/bin/rtl_wmbus exists and are executable.)
|
||||
|
||||
The latest reading of the meter can also be found here: /var/log/wmbusmeters/meter_readings/MyTapWater
|
||||
|
||||
|
@ -70,7 +74,7 @@ The files/dir should then be located here:
|
|||
# Running without config files, good for experimentation and test.
|
||||
|
||||
```
|
||||
wmbusmeters version: 0.9.2
|
||||
wmbusmeters version: 0.9.4
|
||||
Usage: wmbusmeters {options} <device> ( [meter_name] [meter_type] [meter_id] [meter_key] )*
|
||||
|
||||
As <options> you can use:
|
||||
|
@ -91,19 +95,18 @@ As <options> you can use:
|
|||
--useconfig=<dir> load config files from dir/etc
|
||||
--verbose for more information
|
||||
|
||||
As a <device> you can use:
|
||||
As a <device> you can use: auto
|
||||
which will look for the links /dev/im87a,/dev/amb8475 and /dev/rtlsdr (the
|
||||
links are automatically generated by udev if you have run the install scripts.)
|
||||
and start wmbusmeters with the proper tty device or rtlwmbus background process.
|
||||
|
||||
"/dev/ttyUSB" to which a im871a/amb8465 dongle is attached,
|
||||
or you can specify auto and wmbusmeters will look for a suitable dongle
|
||||
on the device links /dev/im871a and /dev/amb8465.
|
||||
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
|
||||
to install the udev rule.
|
||||
|
||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
|
||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
||||
(you might have to tweak 868.95M to nearby frequencies depending
|
||||
on the rtl-sdr dongle you are using)
|
||||
|
||||
"rtlwmbus:<commandline>" to have wmbusmeters spawn
|
||||
that commandline instead, the output is expected to be like rtl_wmbus.
|
||||
As a <device> you can also use: rtlwmbus
|
||||
to spawn the background process: "rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
||||
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
|
||||
be necessary. Or you can specify the entire background process command line: "rtlwmbus:<commandline>"
|
||||
|
||||
As meter quadruples you specify:
|
||||
<meter_name> a mnemonic for this particular meter
|
||||
|
@ -123,7 +126,7 @@ Supported heat cost allocator:
|
|||
Qundis Q caloric (qcaloric)
|
||||
|
||||
Supported electricity meters:
|
||||
Tauron Amiplus (amiplus) (almost complete)
|
||||
Tauron Amiplus (amiplus) (includes vendor apator and echelon)
|
||||
|
||||
Work in progress:
|
||||
Heat meter Kamstrup Multical 302 (multical302)
|
||||
|
@ -132,7 +135,7 @@ Electricity meter Kamstrup Omnipower (omnipower)
|
|||
|
||||
The wmbus dongles imst871a and amb8465 can only listen on one type of wmbus telegrams at a time.
|
||||
So you can listen to multiple meters as long as they all require the same radio mode C1 or T1.
|
||||
If you use rtl-sdr/rtl_wmbus, then you can listen to both C1 and T1 at the same time.
|
||||
If you use rtlwmbus, then you can listen to both C1 and T1 at the same time.
|
||||
|
||||
# Usage examples
|
||||
|
||||
|
@ -160,9 +163,10 @@ Example format json output:
|
|||
|
||||
`{"media":"heat","meter":"multical302","name":"MyHeater","id":"22222222","total_kwh":0.000,"total_volume_m3":0.000,"current_kw":"0.000","timestamp":"2018-02-08T09:07:22Z"}`
|
||||
|
||||
Example format fields output:
|
||||
Example format fields output and use rtlsdr dongle with rtlwmbus tuned to 868.9MHz instead of the
|
||||
default 868.95MHz.
|
||||
|
||||
`wmbusmeters --format=fields auto GreenhouseWater multical21 33333333 ""`
|
||||
`wmbusmeters --format=fields rtlwmbus:868.9M GreenhouseWater multical21 33333333 ""`
|
||||
|
||||
`GreenhouseTapWater;33333333;9999.099;77.712;0.000;11;31;;2018-03-05 12:10.24`
|
||||
|
||||
|
@ -197,7 +201,7 @@ If the meter does not use encryption of its meter data, then enter an empty key
|
|||
`wmbusmeters --format=json --meterfiles auto MyTapWater multical21 12345678 ""`
|
||||
|
||||
If you have a Multical21 meter and you have received a KEM file and its password,
|
||||
from your water municipality, then you can use the XMLExtract.java utility to get
|
||||
from your water municipality, then you can use the utils/XMLExtract.java utility to get
|
||||
the meter key from the KEM file. You need to unzip the the KEM file first though,
|
||||
if it is zipped.
|
||||
|
||||
|
|
|
@ -181,10 +181,13 @@ StopWhenUnneeded=true
|
|||
[Service]
|
||||
Type=forking
|
||||
PrivateTmp=yes
|
||||
#Restart=always
|
||||
RestartSec=1
|
||||
User=wmbusmeters
|
||||
Group=wmbusmeters
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
StartLimitIntervalSec=10
|
||||
StartLimitInterval=10
|
||||
StartLimitBurst=3
|
||||
|
||||
# Run ExecStartPre with root-permissions
|
||||
|
||||
|
@ -219,6 +222,7 @@ then
|
|||
cat <<EOF > $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60",SYMLINK+="im871a",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001",SYMLINK+="amb8465",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838",SYMLINK+="rtlsdr",MODE="0660", GROUP="wmbusmeters",TAG+="systemd",ENV{SYSTEMD_WANTS}="wmbusmeters.service"
|
||||
EOF
|
||||
echo udev: installed $ROOT/etc/udev/rules.d/99-wmbus-usb-serial.rules
|
||||
else
|
||||
|
|
34
src/main.cc
34
src/main.cc
|
@ -68,20 +68,18 @@ As <options> you can use:
|
|||
--useconfig=<dir> load config files from dir/etc
|
||||
--verbose for more information
|
||||
|
||||
As a <device> you can use:
|
||||
As a <device> you can use: auto
|
||||
which will look for the links /dev/im87a,/dev/amb8475 and /dev/rtlsdr (the
|
||||
links are automatically generated by udev if you have run the install scripts.)
|
||||
and start wmbusmeters with the proper tty device or rtlwmbus background process.
|
||||
|
||||
"/dev/ttyUSB" to which a im871a/amb8465 dongle is attached,
|
||||
or you can specify auto and wmbusmeters will look for a suitable dongle
|
||||
on the device links /dev/im871a and /dev/amb8465.
|
||||
As a <device> you can also use: the exact /dev/ttyUSB0 to your dongle if you do not want
|
||||
to install the udev rule.
|
||||
|
||||
"rtlwmbus:868.95M" to have wmbusmeters spawn:
|
||||
"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus"
|
||||
(you might have to tweak 868.95M to nearby frequencies depending
|
||||
on the rtl-sdr dongle you are using, also when run as a daemon,
|
||||
it uses /usr/bin/rtl_sdr and /usr/bin/rtl_wmbus instead.)
|
||||
|
||||
"rtlwmbus:<commandline>" to have wmbusmeters spawn
|
||||
that commandline instead, its output is expected to be like rtl_wmbus.
|
||||
As a <device> you can also use: rtlwmbus
|
||||
to spawn the background process: \"rtl_sdr -f 868.95M -s 1600000 - 2>/dev/null | rtl_wmbus\"
|
||||
You can also use: rtlwmbus:868.9M to use this fq instead. Fq tuning can sometimes
|
||||
be necessary. Or you can specify the entire background process command line: \"rtlwmbus:<commandline>\"
|
||||
|
||||
As meter quadruples you specify:
|
||||
<meter_name> a mnemonic for this particular meter
|
||||
|
@ -195,14 +193,12 @@ void startUsingCommandline(Configuration *config)
|
|||
command = prefix+"rtl_sdr -f "+freq+" -s 16000000 - 2>/dev/null | "+prefix+"rtl_wmbus";
|
||||
}
|
||||
verbose("(rtlwmbus) using command: %s\n", command.c_str());
|
||||
/* This will have to wait until I can differentiate between
|
||||
shell childs that did mqtt/curl exits and the serial shell exit.
|
||||
onChild([command,&manager](){
|
||||
warning("(rtlwmbus) child process exited! Command was: \"%s\"\n", command.c_str());
|
||||
manager.get()->stop();
|
||||
});*/
|
||||
|
||||
wmbus = openRTLWMBUS(command, manager.get());
|
||||
wmbus = openRTLWMBUS(command, manager.get(),
|
||||
[command](){
|
||||
warning("(rtlwmbus) child process exited! "
|
||||
"Command was: \"%s\"\n", command.c_str());
|
||||
});
|
||||
break;
|
||||
}
|
||||
case DEVICE_UNKNOWN:
|
||||
|
|
|
@ -44,7 +44,8 @@ struct SerialCommunicationManagerImp : public SerialCommunicationManager {
|
|||
~SerialCommunicationManagerImp() { }
|
||||
|
||||
unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate);
|
||||
unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs);
|
||||
unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs,
|
||||
function<void()> on_exit);
|
||||
|
||||
void listenTo(SerialDevice *sd, function<void()> cb);
|
||||
void stop();
|
||||
|
@ -69,6 +70,7 @@ private:
|
|||
struct SerialDeviceImp : public SerialDevice {
|
||||
|
||||
int fd() { return fd_; }
|
||||
bool working() { return true; }
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -197,8 +199,11 @@ int SerialDeviceTTY::receive(vector<uchar> *data)
|
|||
return num_read;
|
||||
}
|
||||
|
||||
struct SerialDeviceCommand : public SerialDeviceImp {
|
||||
SerialDeviceCommand(string command, vector<string> args, vector<string> envs, SerialCommunicationManagerImp *manager);
|
||||
struct SerialDeviceCommand : public SerialDeviceImp
|
||||
{
|
||||
SerialDeviceCommand(string command, vector<string> args, vector<string> envs,
|
||||
SerialCommunicationManagerImp *manager,
|
||||
function<void()> on_exit);
|
||||
~SerialDeviceCommand();
|
||||
|
||||
bool open(bool fail_if_not_ok);
|
||||
|
@ -206,6 +211,8 @@ struct SerialDeviceCommand : public SerialDeviceImp {
|
|||
bool send(vector<uchar> &data);
|
||||
int receive(vector<uchar> *data);
|
||||
int fd() { return fd_; }
|
||||
bool working();
|
||||
|
||||
SerialCommunicationManager *manager() { return manager_; }
|
||||
|
||||
private:
|
||||
|
@ -219,16 +226,20 @@ struct SerialDeviceCommand : public SerialDeviceImp {
|
|||
pthread_mutex_t write_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_t read_lock_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
SerialCommunicationManagerImp *manager_;
|
||||
function<void()> on_exit_;
|
||||
};
|
||||
|
||||
SerialDeviceCommand::SerialDeviceCommand(string command,
|
||||
vector<string> args,
|
||||
vector<string> envs,
|
||||
SerialCommunicationManagerImp *manager) {
|
||||
SerialCommunicationManagerImp *manager,
|
||||
function<void()> on_exit)
|
||||
{
|
||||
command_ = command;
|
||||
args_ = args;
|
||||
envs_ = envs;
|
||||
manager_ = manager;
|
||||
on_exit_ = on_exit;
|
||||
}
|
||||
|
||||
SerialDeviceCommand::~SerialDeviceCommand()
|
||||
|
@ -247,6 +258,12 @@ bool SerialDeviceCommand::open(bool fail_if_not_ok)
|
|||
|
||||
void SerialDeviceCommand::close()
|
||||
{
|
||||
if (pid_ == 0 && fd_ == -1) return;
|
||||
if (pid_ && stillRunning(pid_))
|
||||
{
|
||||
stopBackgroundShell(pid_);
|
||||
pid_ = 0;
|
||||
}
|
||||
::flock(fd_, LOCK_UN);
|
||||
::close(fd_);
|
||||
fd_ = -1;
|
||||
|
@ -254,6 +271,16 @@ void SerialDeviceCommand::close()
|
|||
verbose("(serialcmd) closed %s\n", command_.c_str());
|
||||
}
|
||||
|
||||
bool SerialDeviceCommand::working()
|
||||
{
|
||||
if (!pid_) return false;
|
||||
bool r = stillRunning(pid_);
|
||||
if (r) return true;
|
||||
close();
|
||||
on_exit_();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerialDeviceCommand::send(vector<uchar> &data)
|
||||
{
|
||||
if (data.size() == 0) return true;
|
||||
|
@ -328,7 +355,7 @@ SerialCommunicationManagerImp::SerialCommunicationManagerImp(time_t exit_after_s
|
|||
running_ = true;
|
||||
max_fd_ = 0;
|
||||
pthread_create(&thread_, NULL, startLoop, this);
|
||||
//running_ = (rc == 0);
|
||||
wakeMeUpOnSigChld(thread_);
|
||||
start_time_ = time(NULL);
|
||||
exit_after_seconds_ = exit_after_seconds;
|
||||
}
|
||||
|
@ -344,9 +371,10 @@ unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceTTY(st
|
|||
|
||||
unique_ptr<SerialDevice> SerialCommunicationManagerImp::createSerialDeviceCommand(string command,
|
||||
vector<string> args,
|
||||
vector<string> envs)
|
||||
vector<string> envs,
|
||||
function<void()> on_exit)
|
||||
{
|
||||
return unique_ptr<SerialDevice>(new SerialDeviceCommand(command, args, envs, this));
|
||||
return unique_ptr<SerialDevice>(new SerialDeviceCommand(command, args, envs, this, on_exit));
|
||||
}
|
||||
|
||||
void SerialCommunicationManagerImp::listenTo(SerialDevice *sd, function<void()> cb) {
|
||||
|
@ -364,7 +392,7 @@ void SerialCommunicationManagerImp::stop()
|
|||
|
||||
void SerialCommunicationManagerImp::waitForStop()
|
||||
{
|
||||
while (running_) { usleep(1000*1000);}
|
||||
while (running_) { usleep(1000*1000); }
|
||||
pthread_kill(thread_, SIGUSR1);
|
||||
pthread_join(thread_, NULL);
|
||||
for (SerialDevice *d : devices_) {
|
||||
|
@ -405,7 +433,7 @@ void *SerialCommunicationManagerImp::eventLoop() {
|
|||
FD_SET(d->fd(), &readfds);
|
||||
}
|
||||
|
||||
struct timeval timeout { 3600, 0 };
|
||||
struct timeval timeout { 10, 0 };
|
||||
|
||||
if (exit_after_seconds_ > 0) {
|
||||
time_t curr = time(NULL);
|
||||
|
@ -431,6 +459,12 @@ void *SerialCommunicationManagerImp::eventLoop() {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (SerialDevice *d : devices_) {
|
||||
if (!d->working()) {
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
verbose("(serialtty) event loop stopped!\n");
|
||||
return NULL;
|
||||
|
|
11
src/serial.h
11
src/serial.h
|
@ -29,19 +29,24 @@ using namespace std;
|
|||
|
||||
struct SerialCommunicationManager;
|
||||
|
||||
struct SerialDevice {
|
||||
struct SerialDevice
|
||||
{
|
||||
virtual bool open(bool fail_if_not_ok) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual bool send(std::vector<uchar> &data) = 0;
|
||||
virtual int receive(std::vector<uchar> *data) = 0;
|
||||
virtual int fd() = 0;
|
||||
virtual bool working() = 0;
|
||||
virtual SerialCommunicationManager *manager() = 0;
|
||||
virtual ~SerialDevice() = default;
|
||||
};
|
||||
|
||||
struct SerialCommunicationManager {
|
||||
struct SerialCommunicationManager
|
||||
{
|
||||
virtual unique_ptr<SerialDevice> createSerialDeviceTTY(string dev, int baud_rate) = 0;
|
||||
virtual unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args, vector<string> envs) = 0;
|
||||
virtual unique_ptr<SerialDevice> createSerialDeviceCommand(string command, vector<string> args,
|
||||
vector<string> envs,
|
||||
function<void()> on_exit) = 0;
|
||||
virtual void listenTo(SerialDevice *sd, function<void()> cb) = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void waitForStop() = 0;
|
||||
|
|
82
src/shell.cc
82
src/shell.cc
|
@ -37,11 +37,11 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
|||
strcpy(p, program.c_str());
|
||||
argv[0] = p;
|
||||
int i = 1;
|
||||
debug("exec \"%s\"\n", program.c_str());
|
||||
debug("(shell) exec \"%s\"\n", program.c_str());
|
||||
for (auto &a : args) {
|
||||
argv[i] = a.c_str();
|
||||
i++;
|
||||
debug("arg \"%s\"\n", a.c_str());
|
||||
debug("(shell) arg \"%s\"\n", a.c_str());
|
||||
}
|
||||
argv[i] = NULL;
|
||||
|
||||
|
@ -51,7 +51,7 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
|||
for (auto &e : envs) {
|
||||
env[i] = e.c_str();
|
||||
i++;
|
||||
debug("env \"%s\"\n", e.c_str());
|
||||
debug("(shell) env \"%s\"\n", e.c_str());
|
||||
}
|
||||
env[i] = NULL;
|
||||
|
||||
|
@ -68,20 +68,20 @@ void invokeShell(string program, vector<string> args, vector<string> envs)
|
|||
#endif
|
||||
|
||||
perror("Execvp failed:");
|
||||
error("Invoking shell %s failed!\n", program.c_str());
|
||||
error("(shell) invoking %s failed!\n", program.c_str());
|
||||
} else {
|
||||
if (pid == -1) {
|
||||
error("Could not fork!\n");
|
||||
error("(shell) could not fork!\n");
|
||||
}
|
||||
debug("waiting for child %d.\n", pid);
|
||||
debug("(shell) waiting for child %d to complete.\n", pid);
|
||||
// Wait for the child to finish!
|
||||
waitpid(pid, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
// Child exited properly.
|
||||
int rc = WEXITSTATUS(status);
|
||||
debug("%s: return code %d\n", program.c_str(), rc);
|
||||
debug("(shell) %s: return code %d\n", program.c_str(), rc);
|
||||
if (rc != 0) {
|
||||
warning("%s exited with non-zero return code: %d\n", program.c_str(), rc);
|
||||
warning("(shell) %s exited with non-zero return code: %d\n", program.c_str(), rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,11 +96,11 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
|||
strcpy(p, program.c_str());
|
||||
argv[0] = p;
|
||||
int i = 1;
|
||||
debug("exec background \"%s\"\n", program.c_str());
|
||||
debug("(bgshell) exec background \"%s\"\n", program.c_str());
|
||||
for (auto &a : args) {
|
||||
argv[i] = a.c_str();
|
||||
i++;
|
||||
debug("arg \"%s\"\n", a.c_str());
|
||||
debug("(bgshell) arg \"%s\"\n", a.c_str());
|
||||
}
|
||||
argv[i] = NULL;
|
||||
|
||||
|
@ -110,12 +110,12 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
|||
for (auto &e : envs) {
|
||||
env[i] = e.c_str();
|
||||
i++;
|
||||
debug("env \"%s\"\n", e.c_str());
|
||||
debug("(bgshell) env \"%s\"\n", e.c_str());
|
||||
}
|
||||
env[i] = NULL;
|
||||
|
||||
if (pipe(link) == -1) {
|
||||
error("Could not create pipe!\n");
|
||||
error("(bgshell) could not create pipe!\n");
|
||||
}
|
||||
|
||||
*pid = fork();
|
||||
|
@ -138,7 +138,7 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
|||
#endif
|
||||
|
||||
perror("Execvp failed:");
|
||||
warning("Invoking shell %s failed!\n", program.c_str());
|
||||
error("(bgshell) invoking %s failed!\n", program.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -147,21 +147,65 @@ bool invokeBackgroundShell(string program, vector<string> args, vector<string> e
|
|||
return true;
|
||||
}
|
||||
|
||||
bool stillRunning(int pid)
|
||||
{
|
||||
if (pid == 0) return false;
|
||||
int status;
|
||||
int p = waitpid(pid, &status, WNOHANG);
|
||||
if (p == 0) {
|
||||
// The pid has not exited yet.
|
||||
return true;
|
||||
}
|
||||
if (p < 0) {
|
||||
// No pid to wait for.
|
||||
return false;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
// Child exited properly.
|
||||
int rc = WEXITSTATUS(status);
|
||||
debug("(bgshell) %d exited with return code %d\n", pid, rc);
|
||||
}
|
||||
else if (WIFSIGNALED(status)) {
|
||||
// Child forcefully terminated
|
||||
debug("(bgshell) %d terminated due to signal %d\n", pid, WTERMSIG(status));
|
||||
} else
|
||||
{
|
||||
// Exited for other reasons, whatever those may be.
|
||||
debug("(bgshell) %d exited\n", pid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void stopBackgroundShell(int pid)
|
||||
{
|
||||
assert(pid > 0);
|
||||
|
||||
kill(pid, SIGINT);
|
||||
int status;
|
||||
debug("waiting for child %d.\n", pid);
|
||||
int rc = kill(pid, SIGINT);
|
||||
if (rc < 0) {
|
||||
debug("(bgshell) could not sigint pid %d, exited already?\n", pid);
|
||||
return;
|
||||
}
|
||||
// Wait for the child to finish!
|
||||
waitpid(pid, &status, 0);
|
||||
debug("(bgshell) sent sigint, now waiting for child %d to exit.\n", pid);
|
||||
int status;
|
||||
int p = waitpid(pid, &status, 0);
|
||||
if (p < 0) {
|
||||
debug("(bgshell) cannot stop pid %d, exited already?\n", pid);
|
||||
return;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
// Child exited properly.
|
||||
int rc = WEXITSTATUS(status);
|
||||
debug("bgshell: return code %d\n", rc);
|
||||
debug("(bgshell) return code %d\n", rc);
|
||||
if (rc != 0) {
|
||||
warning("bgshell: exited with non-zero return code: %d\n", rc);
|
||||
warning("(bgshell) exited with non-zero return code: %d\n", rc);
|
||||
}
|
||||
}
|
||||
if (WIFSIGNALED(status)) {
|
||||
// Child forcefully terminated
|
||||
debug("(bgshell) %d terminated due to signal %d\n", pid, WTERMSIG(status));
|
||||
} else
|
||||
{
|
||||
debug("(bgshell) %d exited\n", pid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,4 +22,5 @@ using namespace std;
|
|||
|
||||
void invokeShell(string program, vector<string> args, vector<string> envs);
|
||||
bool invokeBackgroundShell(string program, vector<string> args, vector<string> envs, int *out, int *pid);
|
||||
bool stillRunning(int pid);
|
||||
void stopBackgroundShell(int pid);
|
||||
|
|
20
src/util.cc
20
src/util.cc
|
@ -39,19 +39,22 @@ void exitHandler(int signum)
|
|||
if (exit_handler) exit_handler();
|
||||
}
|
||||
|
||||
function<void()> child_handler;
|
||||
pthread_t wake_me_up_on_sig_chld_ {};
|
||||
|
||||
void childProcessDied(int signum)
|
||||
void wakeMeUpOnSigChld(pthread_t t)
|
||||
{
|
||||
if (child_handler) child_handler();
|
||||
wake_me_up_on_sig_chld_ = t;
|
||||
}
|
||||
|
||||
void doNothing(int signum) {
|
||||
void doNothing(int signum)
|
||||
{
|
||||
}
|
||||
|
||||
void onChild(function<void()> cb)
|
||||
void signalMyself(int signum)
|
||||
{
|
||||
child_handler = cb;
|
||||
if (wake_me_up_on_sig_chld_) {
|
||||
pthread_kill(wake_me_up_on_sig_chld_, SIGUSR1);
|
||||
}
|
||||
}
|
||||
|
||||
void onExit(function<void()> cb)
|
||||
|
@ -70,14 +73,15 @@ void onExit(function<void()> cb)
|
|||
sigaction (SIGTERM, NULL, &old_action);
|
||||
if (old_action.sa_handler != SIG_IGN) sigaction (SIGTERM, &new_action, NULL);
|
||||
|
||||
new_action.sa_handler = childProcessDied;
|
||||
new_action.sa_handler = signalMyself;
|
||||
sigemptyset (&new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
sigaction (SIGCHLD, NULL, &old_action);
|
||||
if (old_action.sa_handler != SIG_IGN) sigaction (SIGCHLD, &new_action, NULL);
|
||||
|
||||
new_action.sa_handler = doNothing;
|
||||
sigemptyset (&new_action.sa_mask);
|
||||
new_action.sa_flags = 0;
|
||||
|
||||
sigaction (SIGUSR1, NULL, &old_action);
|
||||
if (old_action.sa_handler != SIG_IGN) sigaction(SIGUSR1, &new_action, NULL);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include<vector>
|
||||
|
||||
void onExit(std::function<void()> cb);
|
||||
void onChild(std::function<void()> cb);
|
||||
void wakeMeUpOnSigChld(pthread_t t);
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
|
|
|
@ -271,6 +271,7 @@ string mediaTypeJSON(int a_field_device_type)
|
|||
|
||||
bool detectIM871A(string device, SerialCommunicationManager *handler);
|
||||
bool detectAMB8465(string device, SerialCommunicationManager *handler);
|
||||
bool detectRTLSDR(string device, SerialCommunicationManager *handler);
|
||||
|
||||
bool existsButWrongGroup(string device)
|
||||
{
|
||||
|
@ -317,6 +318,13 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
|
|||
error("You are not in the same group as the device /dev/amb8465\n");
|
||||
}
|
||||
|
||||
if (detectRTLSDR("/dev/rtlsdr", handler))
|
||||
{
|
||||
return { DEVICE_RTLWMBUS, "rtlwmbus" };
|
||||
} else if (existsButWrongGroup("/dev/rtlsdr")) {
|
||||
error("You are not in the same group as the device /dev/rtlsdr\n");
|
||||
}
|
||||
|
||||
return { DEVICE_UNKNOWN, "" };
|
||||
}
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ pair<MBusDeviceType,string> detectMBusDevice(string device, SerialCommunicationM
|
|||
unique_ptr<WMBus> openIM871A(string device, SerialCommunicationManager *manager);
|
||||
unique_ptr<WMBus> openAMB8465(string device, SerialCommunicationManager *manager);
|
||||
struct WMBusSimulator;
|
||||
unique_ptr<WMBus> openRTLWMBUS(string device, SerialCommunicationManager *manager);
|
||||
unique_ptr<WMBus> openRTLWMBUS(string device, SerialCommunicationManager *manager,
|
||||
std::function<void()> on_exit);
|
||||
unique_ptr<WMBus> openSimulator(string file, SerialCommunicationManager *manager);
|
||||
|
||||
string manufacturer(int m_field);
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
|
||||
#include<assert.h>
|
||||
#include<fcntl.h>
|
||||
#include<grp.h>
|
||||
#include<pthread.h>
|
||||
#include<semaphore.h>
|
||||
#include<string.h>
|
||||
#include<sys/errno.h>
|
||||
#include<sys/stat.h>
|
||||
#include<sys/types.h>
|
||||
#include<unistd.h>
|
||||
|
||||
|
@ -60,13 +62,14 @@ private:
|
|||
SerialCommunicationManager *manager_ {};
|
||||
};
|
||||
|
||||
unique_ptr<WMBus> openRTLWMBUS(string command, SerialCommunicationManager *manager)
|
||||
unique_ptr<WMBus> openRTLWMBUS(string command, SerialCommunicationManager *manager,
|
||||
function<void()> on_exit)
|
||||
{
|
||||
vector<string> args;
|
||||
vector<string> envs;
|
||||
args.push_back("-c");
|
||||
args.push_back(command);
|
||||
auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs);
|
||||
auto serial = manager->createSerialDeviceCommand("/bin/sh", args, envs, on_exit);
|
||||
WMBusRTLWMBUS *imp = new WMBusRTLWMBUS(std::move(serial), manager);
|
||||
return unique_ptr<WMBus>(imp);
|
||||
}
|
||||
|
@ -189,3 +192,20 @@ FrameStatus WMBusRTLWMBUS::checkRTLWMBUSFrame(vector<uchar> &data,
|
|||
|
||||
return FullFrame;
|
||||
}
|
||||
|
||||
bool detectRTLSDR(string device, SerialCommunicationManager *manager)
|
||||
{
|
||||
// No more advanced test than that the /dev/rtlsdr link exists.
|
||||
struct stat sb;
|
||||
int rc = stat(device.c_str(), &sb);
|
||||
if (rc) return false;
|
||||
|
||||
struct group *g = getgrgid(sb.st_gid);
|
||||
if (g && getegid() != g->gr_gid)
|
||||
{
|
||||
// Our group is not the same as the device.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue