Browse Source

Unfinished fixes for konfd daemon

git-svn-id: https://klish.googlecode.com/svn/trunk@373 0eaa4687-2ee9-07dd-09d9-bcdd2d2dd5fb
Serj Kalichev 13 years ago
parent
commit
53982de1a4
1 changed files with 228 additions and 51 deletions
  1. 228 51
      bin/konfd.c

+ 228 - 51
bin/konfd.c

@@ -13,6 +13,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <sys/wait.h>
 #include <errno.h>
 #include <assert.h>
@@ -21,9 +23,12 @@
 #include <string.h>
 #include <sys/select.h>
 #include <signal.h>
+#include <syslog.h>
 #ifdef HAVE_GETOPT_H
 #include <getopt.h>
 #endif
+#include <pwd.h>
+#include <grp.h>
 
 #include "clish/private.h"
 #include "konf/tree.h"
@@ -38,7 +43,7 @@
 #define QUOTE(t) #t
 #define version(v) printf("%s\n", v)
 
-#define KONFD_CONFIG_PATH "/tmp/running-config"
+#define KONFD_PIDFILE "/var/run/konfd.pid"
 
 /* UNIX socket path */
 #ifndef UNIX_PATH_MAX
@@ -60,67 +65,48 @@ static void help(int status, const char *argv0);
 static char * process_query(int sock, konf_tree_t * conf, char *str);
 int answer_send(int sock, char *command);
 static int dump_running_config(int sock, konf_tree_t *conf, konf_query_t *query);
+int daemonize(int nochdir, int noclose);
+struct options *opts_init(void);
+void opts_free(struct options *opts);
+static int opts_parse(int argc, char *argv[], struct options *opts);
+
+/* Command line options */
+struct options {
+	char	*socket_path;
+	char	*pidfile;
+	int	debug; /* Don't daemonize in debug mode */
+	uid_t	uid;
+	gid_t	gid;
+};
 
 /*--------------------------------------------------------- */
 int main(int argc, char **argv)
 {
-	int retval = 0;
+	int retval = -1;
 	unsigned i;
 	char *str;
 	konf_tree_t *conf;
 	lub_bintree_t bufs;
 	konf_buf_t *tbuf;
+	struct options *opts = NULL;
 
 	/* Network vars */
 	int sock;
 	struct sockaddr_un laddr;
 	struct sockaddr_un raddr;
 	fd_set active_fd_set, read_fd_set;
-	const char *socket_path = KONFD_SOCKET_PATH;
 
 	/* Signal vars */
 	struct sigaction sig_act, sigpipe_act;
 	sigset_t sig_set, sigpipe_set;
-	/* Command line options */
-	static const char *shortopts = "hvs:";
-#ifdef HAVE_GETOPT_H
-	static const struct option longopts[] = {
-		{"help",	0, NULL, 'h'},
-		{"version",	0, NULL, 'v'},
-		{"socket",	1, NULL, 's'},
-		{NULL,		0, NULL, 0}
-	};
-#endif
+
+	/* Initialize syslog */
+	openlog(argv[0], LOG_CONS | LOG_PERROR, LOG_DAEMON);
 
 	/* Parse command line options */
-	optind = 0;
-	while(1) {
-		int opt;
-#ifdef HAVE_GETOPT_H
-		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
-#else
-		opt = getopt(argc, argv, shortopts);
-#endif
-		if (-1 == opt)
-			break;
-		switch (opt) {
-		case 's':
-			socket_path = optarg;
-			break;
-		case 'h':
-			help(0, argv[0]);
-			exit(0);
-			break;
-		case 'v':
-			version(VERSION);
-			exit(0);
-			break;
-		default:
-			help(-1, argv[0]);
-			exit(-1);
-			break;
-		}
-	}
+	opts = opts_init();
+	if (opts_parse(argc, argv, opts))
+		goto err;
 
 	/* Set signal handler */
 	sigemptyset(&sig_set);
@@ -145,22 +131,54 @@ int main(int argc, char **argv)
 
 	/* Create listen socket */
 	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
-		fprintf(stderr, "Cannot create socket: %s\n", strerror(errno));
-/*		syslog(LOG_ERR, "Cannot create socket: %s\n", strerror(errno)); */
-		return -1;
+		syslog(LOG_ERR, "Can't create listen socket: %s\n",
+			strerror(errno));
+		goto err;
 	}
 
 	laddr.sun_family = AF_UNIX;
-	strncpy(laddr.sun_path, socket_path, UNIX_PATH_MAX);
+	strncpy(laddr.sun_path, opts->socket_path, UNIX_PATH_MAX);
 	laddr.sun_path[UNIX_PATH_MAX - 1] = '\0';
 	if (bind(sock, (struct sockaddr *)&laddr, sizeof(laddr))) {
-		fprintf(stderr, "Can't bind()\n");
-/*		syslog(LOG_ERR, "Can't bind()\n"); */
-		close(sock);
-		return -1;
+		syslog(LOG_ERR, "Can't bind socket: %s\n",
+			strerror(errno));
+		goto err;
 	}
 	listen(sock, 5);
 
+	if (!opts->debug) {
+		FILE *f_pid = NULL;
+
+		/* Daemonize */
+		if (daemonize(0, 0) < 0) {
+			syslog(LOG_ERR, "Can't daemonize\n");
+			goto err;
+		}
+
+		/* Write pidfile */
+		if ((f_pid = fopen(opts->pidfile, "w")) == NULL) {
+			syslog(LOG_WARNING, "Can't open pidfile %s: %s",
+				opts->pidfile, strerror(errno));
+		} else {
+			if (fprintf(f_pid, "%u\n", getpid()) < 0)
+				syslog(LOG_WARNING, "Can't write to %s: %s",
+					opts->pidfile, strerror(errno));
+			fclose(f_pid);
+		}
+	}
+
+	/* Change UID */
+/*	if (opts.user) {
+		setfsuid(opts.user);
+		if ((setresgid(opts.user, opts.user, opts.user)<0) ||
+			(setresuid(opts.user, opts.user, opts.user)<0)) {
+			syslog(LOG_ERR, "%s", strerror(errno));
+			exit(1);
+		}
+	}
+*/
+
+
 	/* Create configuration tree */
 	conf = konf_tree_new("", 0);
 
@@ -242,9 +260,16 @@ int main(int argc, char **argv)
 		konf_buf_delete(tbuf);
 	}
 
+	retval = 0;
+err:
 	/* Close listen socket */
-	close(sock);
-	unlink(socket_path);
+	if (sock >= 0) {
+		close(sock);
+		unlink(opts->socket_path);
+	}
+
+	/* Free command line options */
+	opts_free(opts);
 
 	return retval;
 }
@@ -415,6 +440,152 @@ static int dump_running_config(int sock, konf_tree_t *conf, konf_query_t *query)
 	return 0;
 }
 
+/*--------------------------------------------------------- */
+/* Implement own simple daemon() to don't use Non-POSIX */
+int daemonize(int nochdir, int noclose)
+{
+	int fd;
+	int pid;
+
+	pid = fork();
+	if (-1 == pid)
+		return -1;
+	if (pid > 0)
+		_exit(0); /* Exit parent */
+	if (setsid() == -1)
+		return -1;
+	if (!nochdir)
+		chdir("/");
+	if (!noclose) {
+		fd = open("/dev/null", O_RDWR, 0);
+		if (fd < 0)
+			return -1;
+		dup2(fd, STDIN_FILENO);
+		dup2(fd, STDOUT_FILENO);
+		dup2(fd, STDERR_FILENO);
+		if (fd > 2)
+			close(fd);
+	}
+
+	return 0;
+}
+
+/*--------------------------------------------------------- */
+/* Initialize option structure by defaults */
+struct options *opts_init(void)
+{
+	struct options *opts = NULL;
+
+	opts = malloc(sizeof(*opts));
+	assert(opts);
+	opts->debug = 0; /* daemonize by default */
+	opts->socket_path = lub_string_dup(KONFD_SOCKET_PATH);
+	opts->pidfile = lub_string_dup(KONFD_PIDFILE);
+
+	return opts;
+}
+
+/*--------------------------------------------------------- */
+/* Free option structure */
+void opts_free(struct options *opts)
+{
+	if (opts->socket_path)
+		lub_string_free(opts->socket_path);
+	if (opts->pidfile)
+		lub_string_free(opts->pidfile);
+	free(opts);
+}
+
+/*--------------------------------------------------------- */
+/* Parse command line options */
+static int opts_parse(int argc, char *argv[], struct options *opts)
+{
+	static const char *shortopts = "hvs:p:u:g:d";
+#ifdef HAVE_GETOPT_H
+	static const struct option longopts[] = {
+		{"help",	0, NULL, 'h'},
+		{"version",	0, NULL, 'v'},
+		{"socket",	1, NULL, 's'},
+		{"pid",		1, NULL, 'p'},
+		{"user",	1, NULL, 'u'},
+		{"group",	1, NULL, 'g'},
+		{"debug",	0, NULL, 'd'},
+		{NULL,		0, NULL, 0}
+	};
+#endif
+	optind = 0;
+	while(1) {
+		int opt;
+#ifdef HAVE_GETOPT_H
+		opt = getopt_long(argc, argv, shortopts, longopts, NULL);
+#else
+		opt = getopt(argc, argv, shortopts);
+#endif
+		if (-1 == opt)
+			break;
+		switch (opt) {
+		case 's':
+			if (opts->socket_path)
+				lub_string_free(opts->socket_path);
+			opts->socket_path = lub_string_dup(optarg);
+			break;
+		case 'p':
+			if (opts->pidfile)
+				lub_string_free(opts->pidfile);
+			opts->pidfile = lub_string_dup(optarg);
+			break;
+		case 'd':
+			opts->debug = 1;
+			break;
+		case 'u': {
+			struct passwd pwd, *result;
+			size_t bufsize;
+			char *buf;
+			int res;
+
+			bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+			if (bufsize == -1)
+				bufsize = 16384;
+			buf = malloc(bufsize);
+			assert(buf);
+			res = getpwnam_r(optarg, &pwd, buf, bufsize, &result);
+			if (!result) {
+				syslog(LOG_ERR, "Can't identify user \"%s\"\n",
+					optarg);
+				return -1;
+			}
+			opts->uid = pwd.pw_uid;
+			free(buf);
+			break;
+		}
+		case 'g': {
+			struct group *grp = getgrnam(optarg);
+			if (!grp) {
+				syslog(LOG_ERR, "Can't identify group \"%s\"\n",
+					optarg);
+				return -1;
+			}
+			opts->gid = grp->gr_gid;
+			break;
+		}
+		case 'h':
+			help(0, argv[0]);
+			exit(0);
+			break;
+		case 'v':
+			version(VERSION);
+			exit(0);
+			break;
+		default:
+			help(-1, argv[0]);
+			exit(-1);
+			break;
+		}
+	}
+
+	return 0;
+}
+
 /*--------------------------------------------------------- */
 /* Print help message */
 static void help(int status, const char *argv0)
@@ -441,7 +612,13 @@ static void help(int status, const char *argv0)
 		printf("Options:\n");
 		printf("\t-v, --version\tPrint version.\n");
 		printf("\t-h, --help\tPrint this help.\n");
+		printf("\t-d, --debug\tDebug mode. Don't daemonize.\n");
 		printf("\t-s <path>, --socket=<path>\tSpecify the UNIX socket "
 			"filesystem path to listen on.\n");
+		printf("\t-p <path>, --pid=<path>\tFile to save daemon's PID to.\n");
+		printf("\t-u <user>, --user=<user>\tExecute process as"
+			" specified user.\n");
+		printf("\t-g <group>, --group=<group>\tExecute process as"
+			" specified group.\n");
 	}
 }