Added deamon mode.
This commit is contained in:
@@ -32,6 +32,8 @@ install: bin/growcontrold
|
|||||||
mkdir -p /opt/growcontrol/bin
|
mkdir -p /opt/growcontrol/bin
|
||||||
mkdir -p /opt/growcontrol/log
|
mkdir -p /opt/growcontrol/log
|
||||||
cp bin/growcontrold /opt/growcontrol/bin
|
cp bin/growcontrold /opt/growcontrol/bin
|
||||||
|
cp -r config /opt/growcontrol
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
rm -rf /opt/growcontrol/bin/growcontrold
|
rm -rf /opt/growcontrol/bin/growcontrold
|
||||||
|
rm -rf /opt/growcontrol/config
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
# DEBUG < INFO < NOTICE < WARN < ERROR < CRIT < ALERT < FATAL = EMERG
|
||||||
|
|
||||||
|
log4j.rootCategory=INFO, logAppender
|
||||||
|
|
||||||
|
#log4j.appender.logAppender=org.apache.log4j.ConsoleAppender
|
||||||
|
#log4j.appender.logAppender.Target= System.err
|
||||||
|
log4j.appender.logAppender=org.apache.log4j.FileAppender
|
||||||
|
log4j.appender.logAppender.fileName=/opt/growcontrol/log/growcontrol.log
|
||||||
|
|
||||||
|
log4j.appender.logAppender.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.logAppender.layout.ConversionPattern=%d [%p] %m%n
|
||||||
|
#log4j.appender.logAppender.layout=org.apache.log4j.BasicLayout
|
||||||
+16
-2
@@ -10,6 +10,10 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
||||||
namespace growcontrol {
|
namespace growcontrol {
|
||||||
|
|
||||||
@@ -38,7 +42,7 @@ bool GrowControl::connect(std::string host , uint16_t port, bool retry) {
|
|||||||
logger << log4cpp::Priority::INFO << "Successfully connected to brickd ("
|
logger << log4cpp::Priority::INFO << "Successfully connected to brickd ("
|
||||||
<< tfConnection.getHost() << ":" << tfConnection.getPort() << ").";
|
<< tfConnection.getHost() << ":" << tfConnection.getPort() << ").";
|
||||||
}
|
}
|
||||||
} while (retry && !breakFlag);
|
} while (retry && !breakFlag && mainLoopFlag);
|
||||||
return breakFlag;
|
return breakFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +107,8 @@ void GrowControl::newDeviceCallback(TfConnection& conn, TfDevice& device) {
|
|||||||
&& device.getDeviceState() == TfDeviceState::AVAILABLE ) {
|
&& device.getDeviceState() == TfDeviceState::AVAILABLE ) {
|
||||||
io16Bricklet = (TfIO16Bricklet*)&device;
|
io16Bricklet = (TfIO16Bricklet*)&device;
|
||||||
io16Bricklet->connect();
|
io16Bricklet->connect();
|
||||||
|
io16Bricklet->setPortConfiguration('a', 0x7F, 'o', false);
|
||||||
|
io16Bricklet->setPort('a', 1);
|
||||||
logger << log4cpp::Priority::INFO << "Added io16 device " << device.getDeviceName(true) << ".";
|
logger << log4cpp::Priority::INFO << "Added io16 device " << device.getDeviceName(true) << ".";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -204,6 +210,12 @@ int GrowControl::getFan1() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GrowControl::shutdownController() {
|
||||||
|
std::lock_guard<std::mutex> lock(controlMutex);
|
||||||
|
mainLoopFlag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void GrowControl::mainLoop() {
|
void GrowControl::mainLoop() {
|
||||||
|
|
||||||
while (mainLoopFlag) {
|
while (mainLoopFlag) {
|
||||||
@@ -241,7 +253,9 @@ void GrowControl::mainLoop() {
|
|||||||
|
|
||||||
lcd.controlMainLoopCallback(sensorValues);
|
lcd.controlMainLoopCallback(sensorValues);
|
||||||
|
|
||||||
sleep(sleepDuration);
|
if (mainLoopFlag) {
|
||||||
|
sleep(sleepDuration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ public:
|
|||||||
double getHum1();
|
double getHum1();
|
||||||
|
|
||||||
int getFan1();
|
int getFan1();
|
||||||
|
|
||||||
|
void shutdownController();
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace growcontrol */
|
} /* namespace growcontrol */
|
||||||
|
|||||||
+49
-39
@@ -101,50 +101,60 @@ void IPCManager::workerThreadFunc(IPCManager* obj, int socket, std::thread* self
|
|||||||
IPCManager::IPCManager() {}
|
IPCManager::IPCManager() {}
|
||||||
|
|
||||||
void IPCManager::init(GrowControl& growControl, std::string socketPath) {
|
void IPCManager::init(GrowControl& growControl, std::string socketPath) {
|
||||||
this->growControl = &growControl;
|
if (!configFlag) {
|
||||||
this->socketPath = socketPath;
|
this->growControl = &growControl;
|
||||||
// Create Socket
|
this->socketPath = socketPath;
|
||||||
if ((socketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
// Create Socket
|
||||||
logger << log4cpp::Priority::ERROR << "Could not create socket";
|
if ((socketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||||
throw GCException("Could not create socket", GCErrorCode::OTHER_ERROR);
|
logger << log4cpp::Priority::ERROR << "Could not create socket";
|
||||||
|
throw GCException("Could not create socket", GCErrorCode::OTHER_ERROR);
|
||||||
|
}
|
||||||
|
// Create address struct
|
||||||
|
struct sockaddr_un local;
|
||||||
|
local.sun_family = AF_UNIX;
|
||||||
|
strncpy(local.sun_path, socketPath.c_str(), sizeof(local.sun_path));
|
||||||
|
// Unlink the socket path (remove possible remains?)
|
||||||
|
unlink(local.sun_path);
|
||||||
|
// bind socket
|
||||||
|
if (bind(socketFD, (struct sockaddr *)&local, sizeof(local)) == -1) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not bind socket";
|
||||||
|
throw GCException("Could not bind socket", GCErrorCode::OTHER_ERROR);
|
||||||
|
}
|
||||||
|
// mark as listen connection
|
||||||
|
if (listen(socketFD, 5) == -1) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not switch socket to accepting mode";
|
||||||
|
throw GCException("Could not switch socket to accepting mode", GCErrorCode::OTHER_ERROR);
|
||||||
|
}
|
||||||
|
logger << log4cpp::Priority::INFO << "Successfully created new ipc socket " << socketPath << ".";
|
||||||
|
listenerThreadFlag = true;
|
||||||
|
listenerThread = new std::thread(listenerThreadFunc, this);
|
||||||
|
configFlag = true;
|
||||||
}
|
}
|
||||||
// Create address struct
|
|
||||||
struct sockaddr_un local;
|
|
||||||
local.sun_family = AF_UNIX;
|
|
||||||
strncpy(local.sun_path, socketPath.c_str(), sizeof(local.sun_path));
|
|
||||||
// Unlink the socket path (remove possible remains?)
|
|
||||||
unlink(local.sun_path);
|
|
||||||
// bind socket
|
|
||||||
if (bind(socketFD, (struct sockaddr *)&local, sizeof(local)) == -1) {
|
|
||||||
logger << log4cpp::Priority::ERROR << "Could not bind socket";
|
|
||||||
throw GCException("Could not bind socket", GCErrorCode::OTHER_ERROR);
|
|
||||||
}
|
|
||||||
// mark as listen connection
|
|
||||||
if (listen(socketFD, 5) == -1) {
|
|
||||||
logger << log4cpp::Priority::ERROR << "Could not switch socket to accepting mode";
|
|
||||||
throw GCException("Could not switch socket to accepting mode", GCErrorCode::OTHER_ERROR);
|
|
||||||
}
|
|
||||||
logger << log4cpp::Priority::INFO << "Successfully created new ipc socket " << socketPath << ".";
|
|
||||||
listenerThreadFlag = true;
|
|
||||||
listenerThread = new std::thread(listenerThreadFunc, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCManager::~IPCManager() {
|
IPCManager::~IPCManager() {
|
||||||
if (socketFD > 0) {
|
shutdown();
|
||||||
close(socketFD);
|
}
|
||||||
unlink(socketPath.c_str());
|
|
||||||
}
|
|
||||||
if (listenerThread != nullptr) {
|
|
||||||
listenerThreadFlag = false;
|
|
||||||
listenerThread->join();
|
|
||||||
delete listenerThread;
|
|
||||||
}
|
|
||||||
std::set<std::thread*> workers(workerThreads);
|
|
||||||
for (auto w : workers) {
|
|
||||||
w->join();
|
|
||||||
delete w;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
void IPCManager::shutdown() {
|
||||||
|
if (configFlag) {
|
||||||
|
if (socketFD > 0) {
|
||||||
|
close(socketFD);
|
||||||
|
unlink(socketPath.c_str());
|
||||||
|
}
|
||||||
|
if (listenerThread != nullptr) {
|
||||||
|
listenerThreadFlag = false;
|
||||||
|
listenerThread->join();
|
||||||
|
delete listenerThread;
|
||||||
|
}
|
||||||
|
std::set<std::thread*> workers(workerThreads);
|
||||||
|
for (auto w : workers) {
|
||||||
|
w->join();
|
||||||
|
delete w;
|
||||||
|
}
|
||||||
|
configFlag = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,11 +28,14 @@ private:
|
|||||||
std::thread* listenerThread = nullptr;
|
std::thread* listenerThread = nullptr;
|
||||||
bool listenerThreadFlag = false;
|
bool listenerThreadFlag = false;
|
||||||
std::set<std::thread*> workerThreads;
|
std::set<std::thread*> workerThreads;
|
||||||
|
bool configFlag = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IPCManager();
|
IPCManager();
|
||||||
virtual ~IPCManager();
|
virtual ~IPCManager();
|
||||||
|
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
void init(GrowControl& growControl, std::string socketPath);
|
void init(GrowControl& growControl, std::string socketPath);
|
||||||
|
|
||||||
static void listenerThreadFunc(IPCManager* obj);
|
static void listenerThreadFunc(IPCManager* obj);
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ SRC_FILES += src/main.cpp \
|
|||||||
src/IPCManager.cpp
|
src/IPCManager.cpp
|
||||||
|
|
||||||
include src/lcdpages/config.mk
|
include src/lcdpages/config.mk
|
||||||
|
include src/third-party/config.mk
|
||||||
|
|||||||
+157
-9
@@ -7,31 +7,179 @@
|
|||||||
|
|
||||||
#include <TfCPPsimple.h>
|
#include <TfCPPsimple.h>
|
||||||
#include "GrowControl.h"
|
#include "GrowControl.h"
|
||||||
|
#include "GCException.h"
|
||||||
#include "IPCManager.h"
|
#include "IPCManager.h"
|
||||||
|
#include "third-party/daemon.h"
|
||||||
|
#include "third-party/pidfile.h"
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace tfcppsimple;
|
using namespace tfcppsimple;
|
||||||
using namespace growcontrol;
|
using namespace growcontrol;
|
||||||
|
|
||||||
|
static log4cpp::Category& logger = log4cpp::Category::getInstance("growcontrol");
|
||||||
|
|
||||||
#define HOST "localhost"
|
std::string pidFile = "/var/run/growcontrold.pid";
|
||||||
#define PORT 4223
|
std::string socketPath = "/var/run/growcontrol.socket";
|
||||||
|
std::string log4cppConfig = "log4cpp.config";
|
||||||
|
std::string host = "localhost";
|
||||||
|
uint16_t port = 4223;
|
||||||
|
bool daemonMode = false;
|
||||||
|
int pid_fd = 0;
|
||||||
|
|
||||||
GrowControl controller;
|
GrowControl controller;
|
||||||
IPCManager ipcManager;
|
IPCManager ipcManager;
|
||||||
|
|
||||||
|
bool termSignalCaught = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void signalHandler(int signum) {
|
||||||
|
signal (signum, signalHandler);
|
||||||
|
switch (signum) {
|
||||||
|
case SIGHUP:
|
||||||
|
case SIGINT:
|
||||||
|
case SIGTERM:
|
||||||
|
logger << log4cpp::Priority::WARN << "Caught term signal, exiting ...";
|
||||||
|
termSignalCaught = true;
|
||||||
|
controller.shutdownController();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void printUsage(std::ostream& stream, std::string name) {
|
||||||
|
stream << "Usage: " << name << " [-d] [-pid pid-file] [-s socket-path] [-l log4cpp-config] [-h host] [-p port]" << std::endl
|
||||||
|
<< "\t-d ... daemon mode" << std::endl
|
||||||
|
<< "\t-pid ... pid-file" << std::endl
|
||||||
|
<< "\t-s ... path of ipc socket" << std::endl
|
||||||
|
<< "\t-l ... path to log4cpp config file" << std::endl
|
||||||
|
<< "\t-h ... brickd address" << std::endl
|
||||||
|
<< "\t-p ... brickd port" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
TfUtils::configureLog4CPP("log4cpp.config");
|
|
||||||
controller.connect(HOST, PORT);
|
for (int t = 1; t < argc; t++) {
|
||||||
sleep(2);
|
if (strcmp(argv[t], "-d") == 0) {
|
||||||
try {
|
daemonMode = true;
|
||||||
ipcManager.init(controller, "/tmp/growcontrol.socket");
|
} else if (strcmp(argv[t], "--help") == 0) {
|
||||||
} catch (GCException &e) {
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(0);
|
||||||
|
} else if (strcmp(argv[t], "-pid") == 0) {
|
||||||
|
if (t+1 < argc && argv[t+1][0] != '-') {
|
||||||
|
t++;
|
||||||
|
pidFile = argv[t];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Option -pid is missing an argument." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[t], "-s") == 0) {
|
||||||
|
if (t+1 < argc && argv[t+1][0] != '-') {
|
||||||
|
t++;
|
||||||
|
socketPath = argv[t];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Option -s is missing an argument." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[t], "-l") == 0) {
|
||||||
|
if (t+1 < argc && argv[t+1][0] != '-') {
|
||||||
|
t++;
|
||||||
|
log4cppConfig = argv[t];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Option -l is missing an argument." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[t], "-h") == 0) {
|
||||||
|
if (t+1 < argc && argv[t+1][0] != '-') {
|
||||||
|
t++;
|
||||||
|
host = argv[t];
|
||||||
|
} else {
|
||||||
|
std::cerr << "Option -h is missing an argument." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[t], "-p") == 0) {
|
||||||
|
if (t+1 < argc && argv[t+1][0] != '-') {
|
||||||
|
t++;
|
||||||
|
port = std::atoi(argv[t]);
|
||||||
|
if (port == 0) {
|
||||||
|
std::cerr << "Option -p has invalid argument \"" << argv[t] << "\"." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Option -p is missing an argument." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unknown argument \"" << argv[t] << "\"." << std::endl << std::endl;
|
||||||
|
printUsage(std::cerr, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TfUtils::configureLog4CPP(log4cppConfig);
|
||||||
|
|
||||||
|
logger << log4cpp::Priority::INFO << "Daemon Mode: " << daemonMode;
|
||||||
|
logger << log4cpp::Priority::INFO << "Pid-File: " << pidFile;
|
||||||
|
logger << log4cpp::Priority::INFO << "socket-path: " << socketPath;
|
||||||
|
logger << log4cpp::Priority::INFO << "log4cpp-config: " << log4cppConfig;
|
||||||
|
logger << log4cpp::Priority::INFO << "Brickd-Address: " << host << ":" << port;
|
||||||
|
|
||||||
|
if (daemonMode) {
|
||||||
|
pid_fd = daemon_start(pidFile.c_str());
|
||||||
|
} else {
|
||||||
|
pid_fd = pidfile_acquire(pidFile.c_str(), getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid_fd >= 0) {
|
||||||
|
|
||||||
|
signal(SIGTERM, signalHandler);
|
||||||
|
signal(SIGINT, signalHandler);
|
||||||
|
signal(SIGHUP, signalHandler);
|
||||||
|
|
||||||
|
controller.connect(host, port);
|
||||||
|
|
||||||
|
if (!termSignalCaught) {
|
||||||
|
sleep(2);
|
||||||
|
try {
|
||||||
|
ipcManager.init(controller, socketPath);
|
||||||
|
} catch (GCException &e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.mainLoop();
|
||||||
|
|
||||||
|
logger << log4cpp::Priority::WARN << "Shutting down ipc ...";
|
||||||
|
|
||||||
|
ipcManager.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger << log4cpp::Priority::WARN << "Problems while forking/writing pid-file.";
|
||||||
|
}
|
||||||
|
|
||||||
|
logger << log4cpp::Priority::WARN << "Shutting down ...";
|
||||||
|
|
||||||
|
if (pid_fd >= 0) {
|
||||||
|
pidfile_release(pidFile.c_str(), pid_fd);
|
||||||
}
|
}
|
||||||
controller.mainLoop();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
SRC_FILES += src/third-party/pidfile.cpp \
|
||||||
|
src/third-party/daemon.cpp
|
||||||
Vendored
+209
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* brickd
|
||||||
|
* Copyright (C) 2012-2013 Matthias Bolte <matthias@tinkerforge.com>
|
||||||
|
*
|
||||||
|
* main_linux.c: Brick Daemon starting point for Linux
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "pidfile.h"
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
|
||||||
|
static log4cpp::Category& logger = log4cpp::Category::getInstance("growcontrol");
|
||||||
|
|
||||||
|
static int daemon_parent(pid_t child, int status_read, const char* _pid_filename) {
|
||||||
|
int8_t status = -1;
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
|
// wait for first child to exit
|
||||||
|
while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (waitpid(pid, NULL, 0) < 0) {
|
||||||
|
fprintf(stderr, "Could not wait for first child process to exit: %s (%d)\n",
|
||||||
|
get_errno_name(errno), errno);
|
||||||
|
|
||||||
|
close(status_pipe[0]);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// wait for second child to start successfully
|
||||||
|
while ((rc = read(status_read, &status, 1)) < 0 && errno == EINTR) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status < 0) {
|
||||||
|
if (rc < 0) {
|
||||||
|
fprintf(stderr, "Could not read from status pipe: %s (%d)\n",
|
||||||
|
strerror(errno), errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(status_read);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(status_read);
|
||||||
|
|
||||||
|
if (status != 1) {
|
||||||
|
if (status == 2) {
|
||||||
|
fprintf(stderr, "Already running according to '%s'\n", _pid_filename);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Second child process exited with an error (status: %d)\n",
|
||||||
|
status);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exit first parent
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int daemon_start(const char* _pid_filename) {
|
||||||
|
int status_pipe[2];
|
||||||
|
pid_t pid;
|
||||||
|
int pid_fd = -1;
|
||||||
|
int8_t status = 0;
|
||||||
|
int null_fd = -1;
|
||||||
|
|
||||||
|
// create status pipe
|
||||||
|
if (pipe(status_pipe) < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not create status pipe: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not fork first child process: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
close(status_pipe[0]);
|
||||||
|
close(status_pipe[1]);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid > 0) { // first parent
|
||||||
|
close(status_pipe[1]);
|
||||||
|
|
||||||
|
daemon_parent(pid, status_pipe[0], _pid_filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// first child, decouple from parent environment
|
||||||
|
close(status_pipe[0]);
|
||||||
|
|
||||||
|
if (chdir("/") < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not change directory to '/': "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
close(status_pipe[1]);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: check error
|
||||||
|
setsid();
|
||||||
|
umask(0);
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not fork second child process: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
close(status_pipe[1]);
|
||||||
|
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid > 0) {
|
||||||
|
// exit second parent
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// second child, write pid
|
||||||
|
pid_fd = pidfile_acquire(_pid_filename, getpid());
|
||||||
|
|
||||||
|
if (pid_fd < 0) {
|
||||||
|
if (pid_fd < -1) {
|
||||||
|
status = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect standard file descriptors
|
||||||
|
null_fd = open("/dev/null", O_RDONLY);
|
||||||
|
|
||||||
|
if (null_fd < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not open /dev/null to redirect: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not redirect stdin: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not redirect stdout: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dup2(null_fd, STDERR_FILENO) != STDERR_FILENO) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not redirect stderr: "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = 1;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (null_fd > STDERR_FILENO) {
|
||||||
|
close(null_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (write(status_pipe[1], &status, 1) < 0 && errno == EINTR) {
|
||||||
|
}
|
||||||
|
|
||||||
|
close(status_pipe[1]);
|
||||||
|
|
||||||
|
return status == 1 ? pid_fd : -1;
|
||||||
|
}
|
||||||
|
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* daemon.h
|
||||||
|
*
|
||||||
|
* Created on: 16.06.2014
|
||||||
|
* Author: matzman
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DAEMON_H_
|
||||||
|
#define DAEMON_H_
|
||||||
|
|
||||||
|
|
||||||
|
int daemon_start(const char* _pid_filename);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* DAEMON_H_ */
|
||||||
Vendored
+112
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* brickd
|
||||||
|
* Copyright (C) 2012-2013 Matthias Bolte <matthias@tinkerforge.com>
|
||||||
|
*
|
||||||
|
* pidfile.c: PID file specific functions
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this functions realize a flock'ed PID file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "pidfile.h"
|
||||||
|
|
||||||
|
#include <log4cpp/Category.hh>
|
||||||
|
|
||||||
|
static log4cpp::Category& logger = log4cpp::Category::getInstance("growcontrol");
|
||||||
|
|
||||||
|
int pidfile_acquire(const char *filename, pid_t pid) {
|
||||||
|
int fd = -1;
|
||||||
|
struct stat stat1;
|
||||||
|
struct stat stat2;
|
||||||
|
struct flock flock;
|
||||||
|
char buffer[64];
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
fd = open(filename, O_WRONLY | O_CREAT, 0644);
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not open PID file '" << filename << "': "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat(fd, &stat1) < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not get status of PID file '" << filename << "': "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
flock.l_type = F_WRLCK;
|
||||||
|
flock.l_whence = SEEK_SET;
|
||||||
|
flock.l_start = 0;
|
||||||
|
flock.l_len = 1;
|
||||||
|
|
||||||
|
if (fcntl(fd, F_SETLK, &flock) < 0) {
|
||||||
|
if (errno != EAGAIN) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not lock PID file '" << filename << "': "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return errno == EAGAIN ? -2 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(filename, &stat2) < 0) {
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat1.st_ino != stat2.st_ino) {
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "%lld", (long long)pid);
|
||||||
|
|
||||||
|
if (write(fd, buffer, strlen(buffer)) < 0) {
|
||||||
|
logger << log4cpp::Priority::ERROR << "Could not write to PID file '" << filename << "': "
|
||||||
|
<< strerror(errno) << " (" << errno << ").";
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pidfile_release(const char *filename, int fd) {
|
||||||
|
unlink(filename);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
Vendored
+28
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* brickd
|
||||||
|
* Copyright (C) 2012-2013 Matthias Bolte <matthias@tinkerforge.com>
|
||||||
|
*
|
||||||
|
* pidfile.h: PID file specific functions
|
||||||
|
*
|
||||||
|
* 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; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BRICKD_PIDFILE_H
|
||||||
|
#define BRICKD_PIDFILE_H
|
||||||
|
|
||||||
|
int pidfile_acquire(const char *filename, pid_t pid);
|
||||||
|
void pidfile_release(const char *filename, int fd);
|
||||||
|
|
||||||
|
#endif // BRICKD_PIDFILE_H
|
||||||
Reference in New Issue
Block a user