mirror of
https://github.com/troglobit/finit.git
synced 2026-10-30 12:50:34 +00:00
Westermo service starter and supervisor, inspired by daemontools
finit.c:
o New service and shell_service stanzas for finit.conf
o Run service_startup() before /etc/finit.d/ hooks
o Run service_monitor() at end of startup
helpers.c:
o Several new functions:
- print_desc() displays a service starting up
- print_result() shows if service started OK or not
- start_process() is used by the services framework
svc.c:
Generic API for managing svc_t structures
ipc.o:
TIPC based framework for communicating with the service monitor.
E.g., to relay (re)configuration signals from a Web/CLI
front-end via finit to one or many setup daemons, and back.
services.c:
Service monitor framework
strlcpy.c:
Secure strncpy() replacement from OpenBSD under the free ISC License.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
1 parent
c0356b5c7a
commit
d21dc2b477
14 files changed
+1352
-28
No files matched your search
@@ -28,7 +28,8 @@ ROOTDIR ?= $(shell pwd)
|
|||||||
VERSION = 1.0-pre
|
VERSION = 1.0-pre
|
||||||
PKG = finit-$(VERSION)
|
PKG = finit-$(VERSION)
|
||||||
EXEC = finit
|
EXEC = finit
|
||||||
OBJS = finit.o helpers.o initctl.o signal.o
|
OBJS = finit.o helpers.o initctl.o signal.o svc.o ipc.o service.o \
|
||||||
|
strlcpy.o
|
||||||
SRCS = $(OBJS:.o=.c)
|
SRCS = $(OBJS:.o=.c)
|
||||||
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
||||||
CFLAGS += -W -Wall -Werror -Os
|
CFLAGS += -W -Wall -Werror -Os
|
||||||
|
|||||||
+9
-2
@@ -1,3 +1,10 @@
|
|||||||
# finit.conf
|
# Simple Debian finit.conf
|
||||||
check /dev/vda1
|
check /dev/vda1
|
||||||
|
module tipc
|
||||||
|
module evdev
|
||||||
|
module loop
|
||||||
|
module psmouse
|
||||||
|
user admin
|
||||||
|
host testbed
|
||||||
|
network service networking start
|
||||||
|
service /usr/sbin/sshd -D -- OpenSSH Server
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
char *sdown = NULL;
|
char *sdown = NULL;
|
||||||
@@ -49,7 +50,7 @@ static char *build_cmd(char *cmd, char *line, int len)
|
|||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
cmd = malloc (strlen(line) + 1);
|
cmd = malloc (strlen(line) + 1);
|
||||||
if (!cmd) {
|
if (!cmd) {
|
||||||
fprintf (stderr, "finit: No memory left for '%s'", line);
|
_e("No memory left for '%s'", line);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
*cmd = 0;
|
*cmd = 0;
|
||||||
@@ -130,7 +131,7 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
cls();
|
cls();
|
||||||
_d("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
|
echo("finit " VERSION " (built " __DATE__ " " __TIME__ " by " WHOAMI ")");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse configuration file
|
* Parse configuration file
|
||||||
@@ -194,6 +195,11 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
startx = build_cmd(NULL, x, CMD_SIZE);
|
startx = build_cmd(NULL, x, CMD_SIZE);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (MATCH_CMD(line, "service ", x)) {
|
||||||
|
if (service_register (x))
|
||||||
|
_e("Failed, too many services to monitor.\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
@@ -283,6 +289,8 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
|
|
||||||
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
#ifdef TOUCH_ETC_NETWORK_RUN_IFSTATE
|
||||||
touch("/etc/network/run/ifstate");
|
touch("/etc/network/run/ifstate");
|
||||||
|
#else
|
||||||
|
remove("/etc/network/run/ifstate");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Set initial hostname. */
|
/* Set initial hostname. */
|
||||||
@@ -325,8 +333,20 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
*/
|
*/
|
||||||
mkdir("/tmp/.X11-unix", 01777);
|
mkdir("/tmp/.X11-unix", 01777);
|
||||||
mkdir("/tmp/.ICE-unix", 01777);
|
mkdir("/tmp/.ICE-unix", 01777);
|
||||||
|
mkdir("/var/run/sshd", 01755);
|
||||||
umask(022);
|
umask(022);
|
||||||
|
|
||||||
|
#ifdef USE_MESSAGE_BUS
|
||||||
|
_d("Starting D-Bus ...");
|
||||||
|
mkdir("/var/run/dbus", 0755);
|
||||||
|
mkdir("/var/lock/subsys/messagebus", 0755);
|
||||||
|
system("dbus-uuidgen --ensure");
|
||||||
|
system("su -c \"dbus-daemon --system\" messagebus");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_d("Starting services from %s", FINIT_CONF);
|
||||||
|
service_startup();
|
||||||
|
|
||||||
#ifdef LISTEN_INITCTL
|
#ifdef LISTEN_INITCTL
|
||||||
listen_initctl();
|
listen_initctl();
|
||||||
#endif
|
#endif
|
||||||
@@ -366,14 +386,6 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
/* ConsoleKit needs this */
|
/* ConsoleKit needs this */
|
||||||
setenv("DISPLAY", ":0", 1);
|
setenv("DISPLAY", ":0", 1);
|
||||||
|
|
||||||
#ifdef USE_MESSAGE_BUS
|
|
||||||
_d("Starting D-Bus ...");
|
|
||||||
mkdir("/var/run/dbus", 0755);
|
|
||||||
mkdir("/var/lock/subsys/messagebus", 0755);
|
|
||||||
system("dbus-uuidgen --ensure");
|
|
||||||
system("su -c \"dbus-daemon --system\" messagebus");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Run startup scripts in /etc/finit.d/, if any. */
|
/* Run startup scripts in /etc/finit.d/, if any. */
|
||||||
run_parts(FINIT_RCSD);
|
run_parts(FINIT_RCSD);
|
||||||
|
|
||||||
@@ -403,13 +415,7 @@ int main(int UNUSED(args), char *argv[])
|
|||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We're the grim reaper of innocent orphaned children ... */
|
service_monitor();
|
||||||
while (1) {
|
|
||||||
sigset_t nmask;
|
|
||||||
|
|
||||||
sigemptyset(&nmask);
|
|
||||||
pselect(0, NULL, NULL, NULL, NULL, &nmask);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,19 +131,26 @@
|
|||||||
#define MATCH_CMD(l, c, x) \
|
#define MATCH_CMD(l, c, x) \
|
||||||
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
(!strncmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||||
|
|
||||||
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0)
|
#define echo(fmt, args...) do { fprintf(stderr, fmt "\n", ##args); } while (0)
|
||||||
|
#define _d(fmt, args...) do { if (debug) { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } } while (0)
|
||||||
|
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt "\n", __func__, ##args); } while (0)
|
||||||
|
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s() - " fmt ". Error %d: %s\n", __func__, ##args, errno, strerror(errno)); } while (0)
|
||||||
|
|
||||||
extern int debug;
|
extern int debug;
|
||||||
extern char *sdown;
|
extern char *sdown;
|
||||||
|
|
||||||
/* signal.c */
|
/* signal.c */
|
||||||
void do_shutdown (int sig);
|
void do_shutdown (int sig);
|
||||||
|
int sig_stopped (void);
|
||||||
void sig_init (void);
|
void sig_init (void);
|
||||||
void sig_setup (void);
|
void sig_setup (void);
|
||||||
|
|
||||||
/* initctl.c */
|
/* initctl.c */
|
||||||
void listen_initctl (void);
|
void listen_initctl (void);
|
||||||
|
|
||||||
|
/* strlcpy.c */
|
||||||
|
size_t strlcpy(char *dst, const char *src, size_t siz);
|
||||||
|
|
||||||
#endif /* FINIT_H_ */
|
#endif /* FINIT_H_ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h> /* isdigit() */
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -78,7 +79,7 @@ void ifconfig(char *name, char *inet, char *mask, int up)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
memset(&ifr, 0, sizeof (ifr));
|
memset(&ifr, 0, sizeof (ifr));
|
||||||
strncpy(ifr.ifr_name, name, IFNAMSIZ);
|
strlcpy(ifr.ifr_name, name, IFNAMSIZ);
|
||||||
a->sin_family = AF_INET;
|
a->sin_family = AF_INET;
|
||||||
|
|
||||||
if (up) {
|
if (up) {
|
||||||
@@ -126,6 +127,95 @@ void copyfile(char *src, char *dst, int size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int print_uptime(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_PRINTK_TIME)
|
||||||
|
FILE * uptimefile;
|
||||||
|
float num1, num2;
|
||||||
|
char uptime_str[30];
|
||||||
|
|
||||||
|
if((uptimefile = fopen("/proc/uptime", "r")) == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
fgets(uptime_str, 20, uptimefile);
|
||||||
|
fclose(uptimefile);
|
||||||
|
|
||||||
|
sscanf(uptime_str, "%f %f", &num1, &num2);
|
||||||
|
sprintf(uptime_str, "[ %.6f ]", num1);
|
||||||
|
|
||||||
|
write(STDERR_FILENO, uptime_str, strlen(uptime_str));
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_descr(char *action, char *descr)
|
||||||
|
{
|
||||||
|
const char home[] = "\r\e[K";
|
||||||
|
const char dots[] = " .....................................................................";
|
||||||
|
|
||||||
|
write(STDERR_FILENO, home, strlen(home));
|
||||||
|
|
||||||
|
print_uptime();
|
||||||
|
|
||||||
|
write(STDERR_FILENO, action, strlen(action));
|
||||||
|
write(STDERR_FILENO, descr, strlen(descr));
|
||||||
|
write(STDERR_FILENO, dots, 60 - strlen(descr) - strlen(action)); /* pad with dots. */
|
||||||
|
}
|
||||||
|
|
||||||
|
int print_result(int fail)
|
||||||
|
{
|
||||||
|
if (fail)
|
||||||
|
fprintf(stderr, " \e[7m[FAIL]\e[0m\n");
|
||||||
|
else
|
||||||
|
fprintf(stderr, " \e[1m[ OK ]\e[0m\n");
|
||||||
|
|
||||||
|
return fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_process(char *cmd, char *args[], int console)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
sigset_t nmask, omask;
|
||||||
|
|
||||||
|
if (sig_stopped())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Block SIGCHLD while forking. */
|
||||||
|
sigemptyset(&nmask);
|
||||||
|
sigaddset(&nmask, SIGCHLD);
|
||||||
|
sigprocmask(SIG_BLOCK, &nmask, &omask);
|
||||||
|
pid = fork();
|
||||||
|
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||||
|
if (!pid) {
|
||||||
|
int i;
|
||||||
|
struct sigaction act;
|
||||||
|
|
||||||
|
/* Reset signal handlers that were set by the parent process */
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
act.sa_handler = SIG_DFL;
|
||||||
|
|
||||||
|
sigemptyset(&nmask);
|
||||||
|
sigaddset(&nmask, SIGCHLD);
|
||||||
|
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||||
|
|
||||||
|
for (i = 1; i < NSIG; i++)
|
||||||
|
sigaction(i, &act, NULL);
|
||||||
|
|
||||||
|
if (console) {
|
||||||
|
int fd = open (CONSOLE, O_WRONLY | O_APPEND);
|
||||||
|
if (-1 != fd) {
|
||||||
|
dup2(STDOUT_FILENO, fd);
|
||||||
|
dup2(STDERR_FILENO, fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execv(cmd, args);
|
||||||
|
exit(!console ? print_result(0) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef BUILTIN_RUNPARTS
|
#ifdef BUILTIN_RUNPARTS
|
||||||
|
|
||||||
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
|
#define NUM_SCRIPTS 128 /* ought to be enough for anyone */
|
||||||
@@ -251,7 +341,106 @@ void set_procname(char *args[], char *name)
|
|||||||
|
|
||||||
prctl(PR_SET_NAME, name, 0, 0, 0);
|
prctl(PR_SET_NAME, name, 0, 0, 0);
|
||||||
memset(args[0], 0, len);
|
memset(args[0], 0, len);
|
||||||
strncpy(args[0], name, len);
|
strlcpy(args[0], name, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_pidname - Find name of a process
|
||||||
|
* @pid: PID of process to find.
|
||||||
|
* @name: Pointer to buffer where to return process name, may be %NULL
|
||||||
|
* @len: Length in bytes of @name buffer.
|
||||||
|
*
|
||||||
|
* If @name is %NULL, or @len is zero the function will return
|
||||||
|
* a static string. This may be useful to one-off calls.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* %NULL on error, otherwise a va
|
||||||
|
*/
|
||||||
|
char *get_pidname(pid_t pid, char *name, size_t len)
|
||||||
|
{
|
||||||
|
int ret = 1;
|
||||||
|
FILE *fp;
|
||||||
|
char path[32];
|
||||||
|
static char line[64];
|
||||||
|
|
||||||
|
if (!name || len == 0)
|
||||||
|
name = line;
|
||||||
|
|
||||||
|
snprintf(path, sizeof(path), "/proc/%d/status", pid);
|
||||||
|
if ((fp = fopen(path, "r")) != NULL) {
|
||||||
|
if (fgets(line, sizeof (line), fp)) {
|
||||||
|
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||||
|
|
||||||
|
chomp(pname);
|
||||||
|
strlcpy(name, pname, len);
|
||||||
|
ret = 0; /* Found it! */
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kill_procname - Send a signal to a process group by name.
|
||||||
|
* @name: Name of process to send signal to.
|
||||||
|
* @signo: Signal to send.
|
||||||
|
*
|
||||||
|
* Send a signal to a running process (group). This function searches
|
||||||
|
* for a given process @name and sends a signal, @signo, to each
|
||||||
|
* process ID matching that @name.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Number of signals sent, i.e. number of processes who have received the signal.
|
||||||
|
*/
|
||||||
|
int kill_procname (const char *name, int signo)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
char path[32], line[64];
|
||||||
|
FILE *fp;
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
dir = opendir("/proc");
|
||||||
|
if (!dir || !name) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((entry = readdir (dir)) != NULL) {
|
||||||
|
/* Skip non-process entries in /proc */
|
||||||
|
if (!isdigit (*entry->d_name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf (path, sizeof(path), "/proc/%s/status", entry->d_name);
|
||||||
|
/* Skip non-readable files (protected?) */
|
||||||
|
if ((fp = fopen (path, "r")) == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fgets (line, sizeof (line), fp)) {
|
||||||
|
char *pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||||
|
|
||||||
|
if (strncmp(pname, name, strlen(pname) - 1) == 0) {
|
||||||
|
int error = errno;
|
||||||
|
|
||||||
|
if (kill(atoi(entry->d_name), signo)) {
|
||||||
|
ERROR("Failed signalling(%d) %s: %s!", signo, name, strerror(error));
|
||||||
|
} else {
|
||||||
|
result++; /* Track number of processes we deliver the signal to. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_hostname(char *hostname)
|
void set_hostname(char *hostname)
|
||||||
|
|||||||
@@ -28,11 +28,16 @@
|
|||||||
int makepath (char *);
|
int makepath (char *);
|
||||||
void ifconfig (char *, char *, char *, int);
|
void ifconfig (char *, char *, char *, int);
|
||||||
void copyfile (char *, char *, int);
|
void copyfile (char *, char *, int);
|
||||||
|
void print_descr (char *action, char *descr);
|
||||||
|
int print_result (int fail);
|
||||||
|
int start_process (char *cmd, char *args[], int console);
|
||||||
void cls (void);
|
void cls (void);
|
||||||
void chomp (char *);
|
void chomp (char *);
|
||||||
int getuser (char *);
|
int getuser (char *);
|
||||||
int getgroup (char *);
|
int getgroup (char *);
|
||||||
void set_procname (char *args[], char *name);
|
void set_procname (char *args[], char *name);
|
||||||
|
char *get_pidname (pid_t pid, char *name, size_t len);
|
||||||
|
int kill_procname (const char *name, int signo);
|
||||||
void set_hostname (char *hostname);
|
void set_hostname (char *hostname);
|
||||||
|
|
||||||
#ifdef BUILTIN_RUNPARTS
|
#ifdef BUILTIN_RUNPARTS
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
/* Generic IPC class based on TIPC as message bus
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <string.h>
|
||||||
|
//#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <linux/tipc.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
#define IPC_MAP_SOCKADDR(a,t,p) \
|
||||||
|
{ \
|
||||||
|
(a).family = AF_TIPC; \
|
||||||
|
(a).addrtype = TIPC_ADDR_MCAST; \
|
||||||
|
(a).scope = TIPC_CLUSTER_SCOPE; \
|
||||||
|
(a).addr.nameseq.type = (t); \
|
||||||
|
(a).addr.nameseq.lower = (p); \
|
||||||
|
(a).addr.nameseq.upper = (p); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipc_init - Create a TIPC socket for receiving or sending events.
|
||||||
|
* @type: The msg bus.
|
||||||
|
* @port: The msg port.
|
||||||
|
* @conn_type: SERVER or CLIENT connection.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The resulting socket on success, -1 on error.
|
||||||
|
*/
|
||||||
|
int ipc_init(int type, int port, int conn_type)
|
||||||
|
{
|
||||||
|
int sd = -1;
|
||||||
|
int restart_count = 0;
|
||||||
|
|
||||||
|
restart:
|
||||||
|
while (-1 == sd) {
|
||||||
|
struct sockaddr_tipc tipc_addr;
|
||||||
|
|
||||||
|
if (restart_count++ > 5) {
|
||||||
|
_e("Failed too many times, aborting.");
|
||||||
|
restart_count = 0;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd = socket(AF_TIPC, SOCK_RDM, 0);
|
||||||
|
if (-1 == sd) {
|
||||||
|
_pe("Failed creating TIPC socket");
|
||||||
|
sleep(1);
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In case the user wants a server socket. */
|
||||||
|
IPC_MAP_SOCKADDR(tipc_addr, type, port);
|
||||||
|
/* bind to the socket if it will work as a server part */
|
||||||
|
if (conn_type) {
|
||||||
|
if (bind
|
||||||
|
(sd, (struct sockaddr *)&tipc_addr,
|
||||||
|
sizeof(tipc_addr))) {
|
||||||
|
_pe("TIPC Port {%u,%u,%u} could not be created",
|
||||||
|
tipc_addr.addr.nameseq.type,
|
||||||
|
tipc_addr.addr.nameseq.lower,
|
||||||
|
tipc_addr.addr.nameseq.upper);
|
||||||
|
close(sd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear counter */
|
||||||
|
restart_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OK, give caller bound socket */
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipc_send_event - Send a TIPC event.
|
||||||
|
* @sd: TIPC descriptor
|
||||||
|
* @event: Data buffer to send.
|
||||||
|
* @sz: Length of @event data.
|
||||||
|
* @bus: ID of tipc bus
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Posix OK(0) on OK or 1 on error.
|
||||||
|
*/
|
||||||
|
int ipc_send_event(int sd, void *event, size_t sz, int bus)
|
||||||
|
{
|
||||||
|
int retries = 0;
|
||||||
|
struct sockaddr_tipc tipc_addr;
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&tipc_addr, 0, sizeof(tipc_addr));
|
||||||
|
tipc_addr.family = AF_TIPC;
|
||||||
|
tipc_addr.addrtype = TIPC_ADDR_MCAST;
|
||||||
|
tipc_addr.addr.nameseq.type = SINGLE_TIPC_NODE;
|
||||||
|
tipc_addr.addr.nameseq.lower = bus;
|
||||||
|
tipc_addr.addr.nameseq.upper = bus;
|
||||||
|
|
||||||
|
while (-1 == sendto(sd, (char *)event, sz, 0, (struct sockaddr *)&tipc_addr, (socklen_t) sizeof(tipc_addr))) {
|
||||||
|
switch (errno) {
|
||||||
|
case EINTR:
|
||||||
|
/* Signal received, resume syscall. */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case ENOTSOCK:
|
||||||
|
case EBADF:
|
||||||
|
if (retries++ > 1) {
|
||||||
|
_e("Too many errors while trying to send.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipc_receive_event - Poll or wait for a TIPC event.
|
||||||
|
* @sd: TIPC descriptor.
|
||||||
|
* @event: Return buffer for the returned link event.
|
||||||
|
* @sz: Size of return buffer.
|
||||||
|
* @timeout: Set to 0 to block, or >0 to have a timeout in milliseconds.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Zero (0) on OK or 1 on error.
|
||||||
|
*/
|
||||||
|
int ipc_receive_event(int sd, void *event, size_t sz, int timeout)
|
||||||
|
{
|
||||||
|
struct sockaddr_tipc tipc_addr;
|
||||||
|
socklen_t alen = sizeof(struct sockaddr_tipc);
|
||||||
|
|
||||||
|
if (!event) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The poll() syscall handles timeout==0 differently */
|
||||||
|
while (timeout > 0) {
|
||||||
|
int ret = 0;
|
||||||
|
struct pollfd fd = { sd, POLLIN | POLLPRI, 0 };
|
||||||
|
|
||||||
|
ret = poll(&fd, 1, timeout);
|
||||||
|
if (0 == ret) {
|
||||||
|
/* Timeout */
|
||||||
|
errno = ETIMEDOUT;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (-1 == ret) {
|
||||||
|
if (EINTR == errno) {
|
||||||
|
/* Unlike the below call to recvfrom() we return here,
|
||||||
|
* because a signal received here could be intended. This
|
||||||
|
* only affects those tasks calling us with a timeout > 0
|
||||||
|
* and they usually know about this, and exploit it.
|
||||||
|
* E.g. by sending kill() to the pthread in charge of link
|
||||||
|
* propagation within a process. One such user is
|
||||||
|
* RSTP. --Jocke 2007-10-15 */
|
||||||
|
_d("poll() received signal...");
|
||||||
|
errno = EINTR;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pe("poll() failed.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check returned events on our TIPC socket. */
|
||||||
|
if (fd.revents & (POLLIN | POLLPRI)) {
|
||||||
|
/* OK reply, activity on sd */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No input, only error. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (-1 == recvfrom(sd, event, sz, 0, (struct sockaddr *)&tipc_addr, &alen)) {
|
||||||
|
switch (errno) {
|
||||||
|
case EINTR:
|
||||||
|
/* Signal received, resume syscall. */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_pe("Failed reading TIPC event");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ipc_wait_event - "Robust" IPC receive, waits with timeout and signal resistance before giving up.
|
||||||
|
* @sd: TIPC descriptor.
|
||||||
|
* @event: Event type to wait for.
|
||||||
|
* @timeout: Set to 0 to block, or >0 to have a timeout in seconds.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Zero (0) on OK or 1 on timeout.
|
||||||
|
*/
|
||||||
|
int ipc_wait_event(int sd, int event, int timeout)
|
||||||
|
{
|
||||||
|
generic_event_t ev;
|
||||||
|
struct timeval start, now, diff;
|
||||||
|
|
||||||
|
/* Poll for activation acknowledgement */
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
do {
|
||||||
|
if (ipc_receive_event(sd, &ev, sizeof(ev), timeout * 1000)) {
|
||||||
|
/* All syscalls may be interrupted by signal, in which case
|
||||||
|
* errno is set to EINTR */
|
||||||
|
if (errno == EINTR) {
|
||||||
|
_d("GOT SIGNAL");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (errno == ETIMEDOUT) {
|
||||||
|
_d("timeout");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Failed with error %d", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Received event 0x%lx, expected 0x%x", ev.mtype, event);
|
||||||
|
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
timersub(&now, &start, &diff);
|
||||||
|
if (diff.tv_sec >= timeout)
|
||||||
|
return 1;
|
||||||
|
} while (ev.mtype != event);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/* Generic IPC class based on TIPC as message bus
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_IPC_H_
|
||||||
|
#define FINIT_IPC_H_
|
||||||
|
|
||||||
|
#define SINGLE_TIPC_NODE 64
|
||||||
|
#define PRIMARY_MESSAGE_BUS 100
|
||||||
|
#define HWSETUP_MESSAGE_BUS 101
|
||||||
|
#define FINIT_MESSAGE_BUS 102
|
||||||
|
|
||||||
|
#define SERVER_CONNECTION 1
|
||||||
|
#define CLIENT_CONNECTION 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generic_event_t - Generic message type.
|
||||||
|
* @mtype: Message type.
|
||||||
|
*
|
||||||
|
* This is the base message type that is used on the system primary message bus. All
|
||||||
|
* messages must start with a long @mtype that describes the type, any extended
|
||||||
|
* messages must add its extra fields after @mtype.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
long mtype;
|
||||||
|
long arg; /* Optional argument, free use. */
|
||||||
|
} generic_event_t;
|
||||||
|
|
||||||
|
int ipc_init (int type, int port, int conn_type);
|
||||||
|
int ipc_receive_event (int sd, void *event, size_t sz, int timeout);
|
||||||
|
int ipc_wait_event (int sd, int event, int timeout);
|
||||||
|
|
||||||
|
#endif /* FINIT_IPC_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,459 @@
|
|||||||
|
/* Service starter and supervisor, inspired by daemontools
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <features.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/stat.h> /* mknod() */
|
||||||
|
#include <sys/wait.h> /* wait() */
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "ipc.h"
|
||||||
|
#include "svc.h"
|
||||||
|
#include "service.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
static int dynamic_event_arg = 0; /* XXX: Fragile construct, refactor! --Jocke */
|
||||||
|
static int sd = -1;
|
||||||
|
|
||||||
|
/* Returns the event type unless it's a reconfiguration event. */
|
||||||
|
static int is_dynamic_event(generic_event_t *event)
|
||||||
|
{
|
||||||
|
if (!event) {
|
||||||
|
_e("Invalid event pointer received.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Hook point for plugins. */
|
||||||
|
|
||||||
|
/* XXX: Fragile construct, refactor! --Jocke */
|
||||||
|
dynamic_event_arg = (int)event->arg;
|
||||||
|
|
||||||
|
return (int)event->mtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return value:
|
||||||
|
* 0 - Disabled
|
||||||
|
* 1 - Enabled
|
||||||
|
* 2 - Enabled, needs restart
|
||||||
|
*/
|
||||||
|
int service_enabled(svc_t *svc, int dynamic)
|
||||||
|
{
|
||||||
|
int run;
|
||||||
|
|
||||||
|
if (!svc->reg.cb) {
|
||||||
|
/* Unknown service, default to enabled. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
run = svc->reg.cb(svc, dynamic);
|
||||||
|
/* Sanity check callback return value */
|
||||||
|
if (run < 0 || run > 2) {
|
||||||
|
_d("Callback for %s returned invalid value %d, disabling svc.", svc->reg.path, run);
|
||||||
|
run = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return run;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This is a map of all known services that we care for. All others, found in
|
||||||
|
* the system finit.conf file, use the default handler. */
|
||||||
|
static svc_map_t known_services[] = {
|
||||||
|
{ NULL, NULL, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used to cleanup any lingering or semi-restarting tasks before respawning. */
|
||||||
|
static int kill_service(svc_t *svc, int signo)
|
||||||
|
{
|
||||||
|
char *name = basename(svc->cmd);
|
||||||
|
|
||||||
|
_d("Sending signal %d to all processes named %s", signo, name);
|
||||||
|
kill_procname(name, signo);
|
||||||
|
|
||||||
|
/* Don't restart immediately, some throttling necessary.
|
||||||
|
* Also it's good form to let the application terminate properly. */
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service_register - Register a non-backgrounding service to be monitored
|
||||||
|
* @line: A complete command line with -- separated description text
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||||
|
*/
|
||||||
|
int service_register(char *line)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
char *cmd, *descr;
|
||||||
|
svc_t *svc = svc_new_service();
|
||||||
|
|
||||||
|
if (!svc || !line) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
descr = strstr(line, "-- ");
|
||||||
|
if (descr) {
|
||||||
|
*descr = 0;
|
||||||
|
strlcpy(svc->descr, descr + 3, sizeof(svc->descr));
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = strtok(line, " ");
|
||||||
|
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||||
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
|
|
||||||
|
while ((cmd = strtok(NULL, " ")))
|
||||||
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
|
|
||||||
|
svc->args[i][0] = 0;
|
||||||
|
|
||||||
|
/* If this is a known service, then register callbacks and other data. */
|
||||||
|
for (i = 0; known_services[i].path; i++) {
|
||||||
|
svc_map_t *curr = &known_services[i];
|
||||||
|
|
||||||
|
if (!strncmp(curr->path, svc->cmd, strlen(svc->cmd))) {
|
||||||
|
memcpy(&svc->reg, curr, sizeof(svc->reg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0 /* May be usefull someday. */
|
||||||
|
static int poll_for_pidfile(char *cmd, const char *path)
|
||||||
|
{
|
||||||
|
int pid = 0;
|
||||||
|
int tries = 0;
|
||||||
|
|
||||||
|
/* Timeout = 100 * 50ms = 5s */
|
||||||
|
while (-1 == access(path, F_OK) && tries++ < 100) {
|
||||||
|
/* Wait 50ms between retries */
|
||||||
|
usleep(50000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-1 == access(path, F_OK)) {
|
||||||
|
_e("Timeout! No PID found for %s, pidfile %s does not exist?", cmd, path);
|
||||||
|
pid = 0;
|
||||||
|
} else {
|
||||||
|
pid = pidfile_read_pid(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* See the README in this package for details on how services are started, stopped
|
||||||
|
* and how the whole process ties together with the rest of the system.
|
||||||
|
* --Jocke 2009-03-18 */
|
||||||
|
/* Remember: service_enabled() must be called before calling service_start() */
|
||||||
|
int service_start(svc_t *svc)
|
||||||
|
{
|
||||||
|
char *args[MAX_NUM_SVC_ARGS];
|
||||||
|
int i = 0, restart = 0;
|
||||||
|
|
||||||
|
/* Ignore if finit is SIGSTOP'ed */
|
||||||
|
if (sig_stopped())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
restart = svc->pid != 0;
|
||||||
|
if (!restart)
|
||||||
|
print_descr("Starting ", svc->descr);
|
||||||
|
while (svc->args[i][0] != 0 && i < MAX_NUM_SVC_ARGS) {
|
||||||
|
args[i] = svc->args[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
args[i] = NULL;
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
_e("(re)starting %s ", svc->cmd);
|
||||||
|
for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++)
|
||||||
|
_e("%s ", args[i]);
|
||||||
|
_e("");
|
||||||
|
}
|
||||||
|
svc->pid = start_process(svc->cmd, args, debug);
|
||||||
|
|
||||||
|
if (!restart)
|
||||||
|
print_result(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tries to find the matching service and start it. */
|
||||||
|
int service_start_by_name(char *name)
|
||||||
|
{
|
||||||
|
svc_t *svc = svc_find_by_name (name);
|
||||||
|
|
||||||
|
if (svc && service_enabled(svc, 0)) {
|
||||||
|
return service_start(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See the README in this package for details on how services are started, reloaded
|
||||||
|
* (config), stopped and how the whole process ties together with the rest of the
|
||||||
|
* system. --Jocke 2009-03-18 */
|
||||||
|
static int stop_service(svc_t *svc)
|
||||||
|
{
|
||||||
|
int res = 1;
|
||||||
|
|
||||||
|
if (!svc) {
|
||||||
|
_e("Failed, no svc pointer.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid <= 1) {
|
||||||
|
_d("bad pid:%d for %s, SIGTERM", svc->pid, svc->descr);
|
||||||
|
svc->pid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_descr("Stopping ", svc->descr);
|
||||||
|
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, get_pidname(svc->pid, NULL, 0));
|
||||||
|
res = kill(svc->pid, SIGTERM);
|
||||||
|
print_result(res);
|
||||||
|
svc->pid = 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See the README in this package for details on how services are started, reloaded
|
||||||
|
* (config), stopped and how the whole process ties together with the rest of the
|
||||||
|
* system. --Jocke 2009-03-18 */
|
||||||
|
static int reload_service(svc_t *svc)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!svc) {
|
||||||
|
_e("Failed, no svc pointer.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->pid <= 1) {
|
||||||
|
_d("Bad PID %d for %s", svc->pid, svc->cmd);
|
||||||
|
svc->pid = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Sending SIGHUP to PID %d", svc->pid);
|
||||||
|
res = kill(svc->pid, SIGHUP);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wait_for_event(generic_event_t *event, int timeout)
|
||||||
|
{
|
||||||
|
if (-1 == sd) {
|
||||||
|
sd = ipc_init(SINGLE_TIPC_NODE, FINIT_MESSAGE_BUS, SERVER_CONNECTION);
|
||||||
|
if (-1 == sd) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ipc_receive_event() might be interrupted by signal, errno is then set to EINTR */
|
||||||
|
return ipc_receive_event(sd, event, sizeof(*event), timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if pid is alive (/proc/pid exists) else 0 */
|
||||||
|
static int pid_alive(unsigned int pid)
|
||||||
|
{
|
||||||
|
char name[24]; /* Enough for max pid_t */
|
||||||
|
|
||||||
|
snprintf(name, sizeof(name), "/proc/%d", pid);
|
||||||
|
return !access(name, F_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void service_startup(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
/* Start finit listen socket before start any services, this to
|
||||||
|
* be sure we don't miss any messages from for example linkd */
|
||||||
|
if (-1 == sd) {
|
||||||
|
sd = ipc_init(SINGLE_TIPC_NODE, FINIT_MESSAGE_BUS, SERVER_CONNECTION);
|
||||||
|
if (-1 == sd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Start 'em */
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
/* Start, when pid==0 we start regardless of ret. */
|
||||||
|
ret = service_enabled(svc, 0);
|
||||||
|
if (ret == 1 || (ret && svc->pid == 0))
|
||||||
|
service_start(svc);
|
||||||
|
else if (ret == 2)
|
||||||
|
reload_service(svc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* XXX: Hook point for plugins. */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_norespawn(void)
|
||||||
|
{
|
||||||
|
return sig_stopped() ||
|
||||||
|
fexist("/mnt/norespawn") ||
|
||||||
|
fexist("/tmp/norespawn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See the README in this package for details on how services are started, stopped
|
||||||
|
* and how the whole process ties together with the rest of the system.
|
||||||
|
* --Jocke 2009-03-18 */
|
||||||
|
void reiterate(generic_event_t *event)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int any_stopped = 0;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
_d("event 0x%x", (unsigned int)event->mtype);
|
||||||
|
/* Reset state variables before each new run. */
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
svc->reload = 0;
|
||||||
|
svc->private = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop services */
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
/* Only do configuration changes here, e.g. stopping disabled services, etc. */
|
||||||
|
if (is_dynamic_event(event) && !svc->reg.dyn_workaround) // XXX: Ugly workaround
|
||||||
|
continue;
|
||||||
|
|
||||||
|
svc->reload = 0;
|
||||||
|
if (svc->pid) {
|
||||||
|
ret = service_enabled(svc, svc->reg.dyn_workaround ? is_dynamic_event(event) : 0);
|
||||||
|
if (2 == ret) {
|
||||||
|
_d("Scheduling %s[%d] for delayed reload.", svc->cmd, svc->pid);
|
||||||
|
svc->reload = 1;
|
||||||
|
} else if (0 == ret) {
|
||||||
|
_d("Attempting to stop %s[%d]", svc->cmd, svc->pid);
|
||||||
|
if (svc->pid && !stop_service(svc)) {
|
||||||
|
any_stopped++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for any stopped processes before notifying hwsetup. */
|
||||||
|
if (any_stopped)
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
/* XXX: Hook point for plugins. */
|
||||||
|
|
||||||
|
/* (Re)Start services */
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
/* Filter out services not interested in dynamic changes. */
|
||||||
|
if (is_dynamic_event(event) && !(svc->reg.dyn_reload || svc->reg.vlans_changed_reload || svc->reg.dyn_workaround))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Check for stale PID! Happens if, e.g., SIGCHLD signal is lost */
|
||||||
|
if (svc->pid && !pid_alive(svc->pid)) {
|
||||||
|
_d("PID:%d (%s) does not seem to be running, scheduling restart.", svc->pid, svc->cmd);
|
||||||
|
svc->pid = 0;
|
||||||
|
svc->reload = 0;
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->reload) {
|
||||||
|
svc->reload = 0;
|
||||||
|
reload_service(svc);
|
||||||
|
} else {
|
||||||
|
int run;
|
||||||
|
restart:
|
||||||
|
run = service_enabled(svc, is_dynamic_event(event));
|
||||||
|
if (run) {
|
||||||
|
if (!svc->pid && !is_norespawn()) {
|
||||||
|
service_start(svc);
|
||||||
|
} else if (2 == run) {
|
||||||
|
reload_service(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Notify configuration frontends that we're done. */
|
||||||
|
if (!is_dynamic_event(event)) {
|
||||||
|
/* XXX: Hook point for plugins. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void service_monitor(void)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
pid_t lost;
|
||||||
|
svc_t *svc;
|
||||||
|
generic_event_t event;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* Poll, timeout 1000 ms, for message from the system, or SIGCHLD
|
||||||
|
* when children dies. */
|
||||||
|
if (!wait_for_event(&event, 1000)) {
|
||||||
|
_d("Received event 0x%lx", event.mtype);
|
||||||
|
/* XXX: Hook point for plugins. */
|
||||||
|
}
|
||||||
|
|
||||||
|
lost = waitpid(-1, &status, WNOHANG);
|
||||||
|
if (fexist(SYNC_SHUTDOWN) || lost <= 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Power user at the console, don't respawn tasks. */
|
||||||
|
if (is_norespawn())
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Monitor 'em */
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
if (lost != svc->pid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sig_stopped()) {
|
||||||
|
_e("Stopped, not respawning killed processes.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Ouch, lost pid %d - %s(%d)", lost, svc->cmd, svc->pid);
|
||||||
|
kill_service(svc, SIGTERM);
|
||||||
|
|
||||||
|
/* Restarting lost service. */
|
||||||
|
if (service_enabled(svc, 0))
|
||||||
|
service_start(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/* Service starter and supervisor, inspired by daemontools
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_SERVICES_H_
|
||||||
|
#define FINIT_SERVICES_H_
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/shm.h> /* shmat() */
|
||||||
|
#include <sys/types.h> /* pid_t */
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
|
||||||
|
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
|
#define MAX_NUM_SVC 64 /* Enough? */
|
||||||
|
#define MAX_NUM_SVC_ARGS 16
|
||||||
|
|
||||||
|
/* Default enable for all services, can be stopped by means
|
||||||
|
* of issuing an initctl call. E.g.
|
||||||
|
* initctl service <stop|start|restart> */
|
||||||
|
typedef struct svc {
|
||||||
|
char cmd[64];
|
||||||
|
char args[MAX_NUM_SVC_ARGS][64];
|
||||||
|
char descr[64];
|
||||||
|
pid_t pid;
|
||||||
|
int reload;
|
||||||
|
int private; /* For svc callbacks to use freely, possibly to store "states". */
|
||||||
|
/* Set for known services, otherwise "empty". */
|
||||||
|
struct svc_map {
|
||||||
|
char *path; /* E.g., "/sbin/snmpd" */
|
||||||
|
int (*cb)(struct svc *, int); /* Service callback, e.g. is_snmpd_enabled() */
|
||||||
|
int dyn_reload; /* Reload on new dynamic event. */
|
||||||
|
int dyn_workaround; /* Ugly/Temporary workaround for dyn-stop/start of serialoverip. */
|
||||||
|
int vlans_changed_reload; /* Reload when VLANs change. */
|
||||||
|
} reg;
|
||||||
|
} svc_t;
|
||||||
|
|
||||||
|
typedef struct svc_map svc_map_t;
|
||||||
|
|
||||||
|
/* Put services array in shm */
|
||||||
|
static inline svc_t *finit_connect_shm(void)
|
||||||
|
{
|
||||||
|
const long ID = FINIT_SHM_ID;
|
||||||
|
static void *ptr = (void *)-1;
|
||||||
|
|
||||||
|
if ((void *)-1 == ptr) {
|
||||||
|
ptr = shmat (shmget (ID, sizeof(svc_t) * MAX_NUM_SVC, 0600 | IPC_CREAT), NULL, 0);
|
||||||
|
if ((void *)-1 == ptr) {
|
||||||
|
_d("Failed allocating shared memory, error %d: %s", errno, strerror (errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (svc_t *)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int service_register (char *line);
|
||||||
|
int service_start (svc_t *svc);
|
||||||
|
int service_start_by_name (char *name);
|
||||||
|
int service_enabled (svc_t *svc, int dynamic);
|
||||||
|
void service_startup (void);
|
||||||
|
void service_monitor (void);
|
||||||
|
|
||||||
|
#endif /* FINIT_SERVICES_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -98,7 +98,8 @@ void do_shutdown (int sig)
|
|||||||
*/
|
*/
|
||||||
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
static void shutdown_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||||
{
|
{
|
||||||
_d("Rebooting on signal %d from pid %d with code %d", sig, info->si_pid, info->si_code);
|
_d("Rebooting on signal %d from %s (PID %d) with code %d",
|
||||||
|
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid, info->si_code);
|
||||||
|
|
||||||
do_shutdown(sig);
|
do_shutdown(sig);
|
||||||
}
|
}
|
||||||
@@ -110,8 +111,8 @@ static void chld_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
|||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
/* XXX: Add (procname) to debug output, add get_procname(pid) to helpers.c */
|
_d("Child %s died, harvesting. Signal %d from pid %d code %d",
|
||||||
_d("Child died, harvesting. Signal %d from pid %d code %d", sig, info->si_pid, info->si_code);
|
get_pidname(info->si_pid, NULL, 0), sig, info->si_pid, info->si_code);
|
||||||
while (waitpid(-1, &status, WNOHANG) != 0) {
|
while (waitpid(-1, &status, WNOHANG) != 0) {
|
||||||
if (errno == ECHILD)
|
if (errno == ECHILD)
|
||||||
break;
|
break;
|
||||||
@@ -123,17 +124,27 @@ static void chld_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
|||||||
*/
|
*/
|
||||||
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
static void sigstop_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||||
{
|
{
|
||||||
_d("Received SIGSTOP(%d) from pid %d code %d", sig, info->si_pid, info->si_code);
|
_d("Received SIGSTOP(%d) from %s (PID %d) code %d",
|
||||||
|
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid, info->si_code);
|
||||||
touch(SYNC_STOPPED);
|
touch(SYNC_STOPPED);
|
||||||
stopped ++;
|
stopped ++;
|
||||||
}
|
}
|
||||||
static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
static void sigcont_handler(int sig, siginfo_t *info, void *UNUSED(ctx))
|
||||||
{
|
{
|
||||||
_d("Received SIGCONT(%d) from pid %d code %d", sig, info->si_pid, info->si_code);
|
_d("Received SIGCONT(%d) from %s (PID %d) code %d",
|
||||||
|
sig, get_pidname(info->si_pid, NULL, 0), info->si_pid, info->si_code);
|
||||||
stopped = 0;
|
stopped = 0;
|
||||||
remove(SYNC_STOPPED);
|
remove(SYNC_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is SIGSTOP asserted?
|
||||||
|
*/
|
||||||
|
int sig_stopped(void)
|
||||||
|
{
|
||||||
|
return stopped;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inital signal setup - ignore everything until we're capable of responding
|
* Inital signal setup - ignore everything until we're capable of responding
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy src to string dst of size siz. At most siz-1 characters
|
||||||
|
* will be copied. Always NUL terminates (unless siz == 0).
|
||||||
|
* Returns strlen(src); if retval >= siz, truncation occurred.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcpy(char *dst, const char *src, size_t siz)
|
||||||
|
{
|
||||||
|
char *d = dst;
|
||||||
|
const char *s = src;
|
||||||
|
size_t n = siz;
|
||||||
|
|
||||||
|
/* Copy as many bytes as will fit */
|
||||||
|
if (n != 0) {
|
||||||
|
while (--n != 0) {
|
||||||
|
if ((*d++ = *s++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||||
|
if (n == 0) {
|
||||||
|
if (siz != 0)
|
||||||
|
*d = '\0'; /* NUL-terminate dst */
|
||||||
|
while (*s++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(s - src - 1); /* count does not include NUL */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* c-file-style: "bsd"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/* Generic API for managing svc_t structures
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "svc.h"
|
||||||
|
|
||||||
|
/* The registered services are kept in shared memory for easy read-only access
|
||||||
|
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
|
||||||
|
* liveness state of all daemons that should run. */
|
||||||
|
static int svc_counter = 0; /* Number of registered services to monitor. */
|
||||||
|
static svc_t *services = NULL; /* List of registered services, in shared memory. */
|
||||||
|
|
||||||
|
/* Connect to/create shared memory area of registered servies */
|
||||||
|
static void __connect_shm(void)
|
||||||
|
{
|
||||||
|
if (!services) {
|
||||||
|
services = finit_connect_shm();
|
||||||
|
if (!services) {
|
||||||
|
/* This should never happen, but if it does we're probably
|
||||||
|
* knee-deep in more serious problems already... */
|
||||||
|
_e("Failed setting up shared memory for service monitor.\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_t *svc_new_service (void)
|
||||||
|
{
|
||||||
|
svc_t *svc = NULL;
|
||||||
|
|
||||||
|
__connect_shm();
|
||||||
|
if (svc_counter < MAX_NUM_SVC) {
|
||||||
|
svc = &services[svc_counter++];
|
||||||
|
memset(svc, 0, sizeof(*svc));
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_t *svc_find_by_id (int id)
|
||||||
|
{
|
||||||
|
__connect_shm();
|
||||||
|
if (id >= svc_counter)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &services[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_t *svc_find_by_name (char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
__connect_shm();
|
||||||
|
for (i = 0; i < svc_counter; i++) {
|
||||||
|
svc_t *svc = &services[i];
|
||||||
|
|
||||||
|
if (!strncmp(name, svc->cmd, strlen(svc->cmd))) {
|
||||||
|
_d("Found a matching svc for %s", name);
|
||||||
|
return svc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterates over all registered services. Get first svc_t by setting @restart */
|
||||||
|
svc_t *svc_iterator (int restart)
|
||||||
|
{
|
||||||
|
static int id = 0;
|
||||||
|
|
||||||
|
__connect_shm();
|
||||||
|
if (restart)
|
||||||
|
id = 0;
|
||||||
|
|
||||||
|
if (id >= svc_counter)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &services[id++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* compile-command: "gcc -g -DUNITTEST -o unittest svc.c"
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/* Generic API for managing svc_t structures
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||||
|
* Copyright (c) 2008-2012 Joachim Nilsson <troglobit@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FINIT_SVC_H__
|
||||||
|
#define FINIT_SVC_H__
|
||||||
|
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
svc_t *svc_new_service (void);
|
||||||
|
svc_t *svc_find_by_id (int id);
|
||||||
|
svc_t *svc_find_by_name (char *name);
|
||||||
|
svc_t *svc_iterator (int restart);
|
||||||
|
|
||||||
|
#endif /* FINIT_SVC_H__ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
Reference in new issue
Block a user