Added deamon mode.

This commit is contained in:
hg
2014-06-17 01:16:19 +02:00
parent 230897c182
commit 3992e5f7d0
13 changed files with 610 additions and 50 deletions
+2
View File
@@ -32,6 +32,8 @@ install: bin/growcontrold
mkdir -p /opt/growcontrol/bin
mkdir -p /opt/growcontrol/log
cp bin/growcontrold /opt/growcontrol/bin
cp -r config /opt/growcontrol
uninstall:
rm -rf /opt/growcontrol/bin/growcontrold
rm -rf /opt/growcontrol/config
+13
View File
@@ -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
View File
@@ -10,6 +10,10 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
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 ("
<< tfConnection.getHost() << ":" << tfConnection.getPort() << ").";
}
} while (retry && !breakFlag);
} while (retry && !breakFlag && mainLoopFlag);
return breakFlag;
}
@@ -103,6 +107,8 @@ void GrowControl::newDeviceCallback(TfConnection& conn, TfDevice& device) {
&& device.getDeviceState() == TfDeviceState::AVAILABLE ) {
io16Bricklet = (TfIO16Bricklet*)&device;
io16Bricklet->connect();
io16Bricklet->setPortConfiguration('a', 0x7F, 'o', false);
io16Bricklet->setPort('a', 1);
logger << log4cpp::Priority::INFO << "Added io16 device " << device.getDeviceName(true) << ".";
}
break;
@@ -204,6 +210,12 @@ int GrowControl::getFan1() {
}
void GrowControl::shutdownController() {
std::lock_guard<std::mutex> lock(controlMutex);
mainLoopFlag = false;
}
void GrowControl::mainLoop() {
while (mainLoopFlag) {
@@ -241,7 +253,9 @@ void GrowControl::mainLoop() {
lcd.controlMainLoopCallback(sensorValues);
sleep(sleepDuration);
if (mainLoopFlag) {
sleep(sleepDuration);
}
}
}
+2
View File
@@ -72,6 +72,8 @@ public:
double getHum1();
int getFan1();
void shutdownController();
};
} /* namespace growcontrol */
+49 -39
View File
@@ -101,50 +101,60 @@ void IPCManager::workerThreadFunc(IPCManager* obj, int socket, std::thread* self
IPCManager::IPCManager() {}
void IPCManager::init(GrowControl& growControl, std::string socketPath) {
this->growControl = &growControl;
this->socketPath = socketPath;
// Create Socket
if ((socketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
logger << log4cpp::Priority::ERROR << "Could not create socket";
throw GCException("Could not create socket", GCErrorCode::OTHER_ERROR);
if (!configFlag) {
this->growControl = &growControl;
this->socketPath = socketPath;
// Create Socket
if ((socketFD = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
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() {
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;
}
shutdown();
}
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;
}
}
+3
View File
@@ -28,11 +28,14 @@ private:
std::thread* listenerThread = nullptr;
bool listenerThreadFlag = false;
std::set<std::thread*> workerThreads;
bool configFlag = false;
public:
IPCManager();
virtual ~IPCManager();
void shutdown();
void init(GrowControl& growControl, std::string socketPath);
static void listenerThreadFunc(IPCManager* obj);
+1
View File
@@ -5,3 +5,4 @@ SRC_FILES += src/main.cpp \
src/IPCManager.cpp
include src/lcdpages/config.mk
include src/third-party/config.mk
+157 -9
View File
@@ -7,31 +7,179 @@
#include <TfCPPsimple.h>
#include "GrowControl.h"
#include "GCException.h"
#include "IPCManager.h"
#include "third-party/daemon.h"
#include "third-party/pidfile.h"
#include <log4cpp/Category.hh>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>
using namespace tfcppsimple;
using namespace growcontrol;
static log4cpp::Category& logger = log4cpp::Category::getInstance("growcontrol");
#define HOST "localhost"
#define PORT 4223
std::string pidFile = "/var/run/growcontrold.pid";
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;
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) {
TfUtils::configureLog4CPP("log4cpp.config");
controller.connect(HOST, PORT);
sleep(2);
try {
ipcManager.init(controller, "/tmp/growcontrol.socket");
} catch (GCException &e) {
for (int t = 1; t < argc; t++) {
if (strcmp(argv[t], "-d") == 0) {
daemonMode = true;
} else if (strcmp(argv[t], "--help") == 0) {
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();
}
+3
View File
@@ -0,0 +1,3 @@
SRC_FILES += src/third-party/pidfile.cpp \
src/third-party/daemon.cpp
+209
View File
@@ -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;
}
+15
View File
@@ -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_ */
+112
View File
@@ -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);
}
+28
View File
@@ -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