diff options
author | Jan Safranek <jsafrane@redhat.com> | 2009-03-13 15:16:19 +0100 |
---|---|---|
committer | Jan Safranek <jsafrane@redhat.com> | 2009-03-26 09:34:18 +0100 |
commit | f8e05fc8c129a13fed256b03a23537ef94c77152 (patch) | |
tree | c64ea7d9f7daeefd307feec1bcb90ea5e3e6d600 /src/daemon | |
parent | 04bb98f8bd9751dd8a514b0e3a6c4862ceabeae9 (diff) | |
download | libcg-f8e05fc8c129a13fed256b03a23537ef94c77152.tar.gz libcg-f8e05fc8c129a13fed256b03a23537ef94c77152.tar.xz libcg-f8e05fc8c129a13fed256b03a23537ef94c77152.zip |
Distribute files to various subdirectories
Signed-off-by: Jan Safranek <jsafrane@redhat.com>
Diffstat (limited to 'src/daemon')
-rw-r--r-- | src/daemon/cgrulesengd.c | 793 | ||||
-rw-r--r-- | src/daemon/cgrulesengd.h | 127 |
2 files changed, 920 insertions, 0 deletions
diff --git a/src/daemon/cgrulesengd.c b/src/daemon/cgrulesengd.c new file mode 100644 index 0000000..8efdce1 --- /dev/null +++ b/src/daemon/cgrulesengd.c @@ -0,0 +1,793 @@ +/* + * Copyright Red Hat Inc. 2008 + * + * Author: Steve Olivieri <sjo@redhat.com> + * Author: Vivek Goyal <vgoyal@redhat.com> + * + * Some part of the programs have been derived from Dhaval Giani's posting + * for daemon to place the task in right container. Original copyright notice + * follows. + * + * Copyright IBM Corporation, 2007 + * Author: Dhaval Giani <dhaval <at> linux.vnet.ibm.com> + * Derived from test_cn_proc.c by Matt Helsley + * Original copyright notice follows + * + * Copyright (C) Matt Helsley, IBM Corp. 2005 + * Derived from fcctl.c by Guillaume Thouvenin + * Original copyright notice follows: + * + * Copyright (C) 2005 BULL SA. + * Written by Guillaume Thouvenin <guillaume.thouvenin <at> bull.net> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * TODO Stop using netlink for communication (or at least rewrite that part). + */ + +#include "libcgroup.h" +#include "cgrulesengd.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <string.h> +#include <linux/netlink.h> +#include <signal.h> +#include <time.h> +#include <syslog.h> +#include <getopt.h> + +#include <sys/stat.h> +#include <unistd.h> +#include <linux/connector.h> +#include <linux/cn_proc.h> + +/* Log file, NULL if logging to file is disabled */ +FILE* logfile; + +/* Log facility, 0 if logging to syslog is disabled */ +int logfacility; + +/* Current log level */ +int loglevel; + +/** + * Prints the usage information for this program and, optionally, an error + * message. This function uses vfprintf. + * @param fd The file stream to print to + * @param msg The error message to print (printf style) + * @param ... Any args to msg (printf style) + */ +void usage(FILE* fd, const char* msg, ...) +{ + /* List of args to msg */ + va_list ap; + + /* Put all args after msg into the list. */ + va_start(ap, msg); + + if (msg) + vfprintf(fd, msg, ap); + fprintf(fd, "\n"); + fprintf(fd, "cgrulesengd -- a daemon for the cgroups rules engine\n\n"); + fprintf(fd, "Usage : cgrulesengd [options]\n\n"); + fprintf(fd, " options :\n"); + fprintf(fd, " -q | --quiet quiet mode\n" + " -v | --verbose verbose mode\n" + " -f <path> | --logfile=<path> write log to file\n" + " -s[facility] | --syslog=[facility] write log to syslog\n" + " -n | --nodaemom don't fork daemon\n" + " -d | --debug same as -v -v -n -f -\n" + " -Q | --nolog disable logging\n" + " -h | --help show this help\n\n" + ); + va_end(ap); +} + +/** + * Prints a formatted message (like printf()) to all log destinations. + * Flushes the file stream's buffer so that the message is immediately + * readable. + * @param level The log level (LOG_EMERG ... LOG_DEBUG) + * @param format The format for the message (printf style) + * @param ... Any args to format (printf style) + */ +void flog(int level, const char *format, ...) +{ + /* List of args to format */ + va_list ap; + + /* Check the log level */ + if (level > loglevel) + return; + + if (logfile) { + /* Print the message to the given stream. */ + va_start(ap, format); + vfprintf(logfile, format, ap); + va_end(ap); + fprintf(logfile, "\n"); + + /* + * Flush the stream's buffer, so the data is readable + * immediately. + */ + fflush(logfile); + } + + if (logfacility) { + va_start(ap, format); + vsyslog(LOG_MAKEPRI(logfacility, level), format, ap); + va_end(ap); + } +} + +/** + * Process an event from the kernel, and determine the correct UID/GID/PID to + * pass to libcgroup. Then, libcgroup will decide the cgroup to move the PID + * to, if any. + * @param ev The event to process + * @param type The type of event to process (part of ev) + * @return 0 on success, > 0 on failure + */ +int cgre_process_event(const struct proc_event *ev, const int type) +{ + /* Handle for the /proc/PID/status file */ + FILE *f; + + /* Path for /proc/PID/status file */ + char path[FILENAME_MAX]; + + /* Temporary buffer */ + char *buf = NULL; + + /* UID data */ + uid_t ruid, euid, suid, fsuid, log_uid = 0; + + /* GID data */ + gid_t rgid, egid, sgid, fsgid, log_gid = 0; + + /* PID, just for logging */ + pid_t log_pid = 0; + + /* Return codes */ + int ret = 0; + + /* + * First, we need to open the /proc/PID/status file so that we can + * get the effective UID and GID for the process that we're working + * on. This process is probably not us, so we can't just call + * geteuid() or getegid(). + */ + sprintf(path, "/proc/%d/status", ev->event_data.id.process_pid); + f = fopen(path, "r"); + if (!f) { + flog(LOG_WARNING, "Failed to open %s", path); + goto finished; + } + + /* Now, we need to find either the eUID or the eGID of the process. */ + buf = calloc(4096, sizeof(char)); + if (!buf) { + flog(LOG_WARNING, "Failed to process event, out of" + "memory? Error: %s", + strerror(errno)); + ret = errno; + fclose(f); + goto finished; + } + switch (type) { + case PROC_EVENT_UID: + /* Have the eUID, need to find the eGID. */ + while (fgets(buf, 4096, f)) { + if (!strncmp(buf, "Gid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", &rgid, &egid, + &sgid, &fsgid); + break; + } + memset(buf, '\0', 4096); + } + break; + case PROC_EVENT_GID: + /* Have the eGID, need to find the eUID. */ + while (fgets(buf, 4096, f)) { + if (!strncmp(buf, "Uid:", 4)) { + sscanf((buf + 5), "%d%d%d%d", &ruid, &euid, + &suid, &fsuid); + break; + } + memset(buf, '\0', 4096); + } + break; + default: + flog(LOG_WARNING, "For some reason, we're processing a" + " non-UID/GID event. Something is wrong!"); + break; + } + free(buf); + fclose(f); + + /* + * Now that we have the UID, the GID, and the PID, we can make a call + * to libcgroup to change the cgroup for this PID. + */ + switch (type) { + case PROC_EVENT_UID: + log_uid = ev->event_data.id.e.euid; + log_gid = egid; + log_pid = ev->event_data.id.process_pid; + ret = cgroup_change_cgroup_uid_gid_flags( + ev->event_data.id.e.euid, + egid, ev->event_data.id.process_pid, + CGFLAG_USECACHE); + break; + case PROC_EVENT_GID: + log_uid = euid; + log_gid = ev->event_data.id.e.egid; + log_pid = ev->event_data.id.process_pid; + ret = cgroup_change_cgroup_uid_gid_flags(euid, + ev->event_data.id.e.egid, + ev->event_data.id.process_pid, + CGFLAG_USECACHE); + break; + default: + break; + } + + if (ret) { + /* + * TODO: add some supression, do not spam log when every group + * change fails + */ + flog(LOG_WARNING, "Cgroup change for PID: %d, UID: %d, GID: %d" + " FAILED! (Error Code: %d)", log_pid, log_uid, log_gid, + ret); + } else { + flog(LOG_INFO, "Cgroup change for PID: %d, UID: %d, GID: %d OK", + log_pid, log_uid, log_gid); + } + +finished: + return ret; +} + +/** + * Handle a netlink message. In the event of PROC_EVENT_UID or PROC_EVENT_GID, + * we pass the event along to cgre_process_event for further processing. All + * other events are ignored. + * @param cn_hdr The netlink message + * @return 0 on success, > 0 on error + */ +int cgre_handle_msg(struct cn_msg *cn_hdr) +{ + /* The event to consider */ + struct proc_event *ev; + + /* Return codes */ + int ret = 0; + + /* Get the event data. We only care about two event types. */ + ev = (struct proc_event*)cn_hdr->data; + switch (ev->what) { + case PROC_EVENT_UID: + flog(LOG_DEBUG, "UID Event: PID = %d, tGID = %d, rUID = %d," + " eUID = %d", ev->event_data.id.process_pid, + ev->event_data.id.process_tgid, + ev->event_data.id.r.ruid, + ev->event_data.id.e.euid); + ret = cgre_process_event(ev, PROC_EVENT_UID); + break; + case PROC_EVENT_GID: + flog(LOG_DEBUG, "GID Event: PID = %d, tGID = %d, rGID = %d," + " eGID = %d", ev->event_data.id.process_pid, + ev->event_data.id.process_tgid, + ev->event_data.id.r.rgid, + ev->event_data.id.e.egid); + ret = cgre_process_event(ev, PROC_EVENT_GID); + break; + default: + break; + } + + return ret; +} + +int cgre_create_netlink_socket_process_msg() +{ + int sk_nl; + int err; + struct sockaddr_nl my_nla, kern_nla, from_nla; + socklen_t from_nla_len; + char buff[BUFF_SIZE]; + int rc = -1; + struct nlmsghdr *nl_hdr; + struct cn_msg *cn_hdr; + enum proc_cn_mcast_op *mcop_msg; + size_t recv_len = 0; + + /* + * Create an endpoint for communication. Use the kernel user + * interface device (PF_NETLINK) which is a datagram oriented + * service (SOCK_DGRAM). The protocol used is the connector + * protocol (NETLINK_CONNECTOR) + */ + sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (sk_nl == -1) { + cgroup_dbg("socket sk_nl error"); + return rc; + } + + my_nla.nl_family = AF_NETLINK; + my_nla.nl_groups = CN_IDX_PROC; + my_nla.nl_pid = getpid(); + my_nla.nl_pad = 0; + + kern_nla.nl_family = AF_NETLINK; + kern_nla.nl_groups = CN_IDX_PROC; + kern_nla.nl_pid = 1; + kern_nla.nl_pad = 0; + + err = bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla)); + if (err == -1) { + cgroup_dbg("binding sk_nl error"); + goto close_and_exit; + } + + nl_hdr = (struct nlmsghdr *)buff; + cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); + mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0]; + cgroup_dbg("sending proc connector: PROC_CN_MCAST_LISTEN... "); + memset(buff, 0, sizeof(buff)); + *mcop_msg = PROC_CN_MCAST_LISTEN; + + /* fill the netlink header */ + nl_hdr->nlmsg_len = SEND_MESSAGE_LEN; + nl_hdr->nlmsg_type = NLMSG_DONE; + nl_hdr->nlmsg_flags = 0; + nl_hdr->nlmsg_seq = 0; + nl_hdr->nlmsg_pid = getpid(); + + /* fill the connector header */ + cn_hdr->id.idx = CN_IDX_PROC; + cn_hdr->id.val = CN_VAL_PROC; + cn_hdr->seq = 0; + cn_hdr->ack = 0; + cn_hdr->len = sizeof(enum proc_cn_mcast_op); + cgroup_dbg("sending netlink message len=%d, cn_msg len=%d\n", + nl_hdr->nlmsg_len, (int) sizeof(struct cn_msg)); + if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) { + cgroup_dbg("failed to send proc connector mcast ctl op!\n"); + goto close_and_exit; + } + cgroup_dbg("sent\n"); + + for(memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla); + ; memset(buff, 0, sizeof(buff)), from_nla_len = sizeof(from_nla)) { + struct nlmsghdr *nlh = (struct nlmsghdr*)buff; + memcpy(&from_nla, &kern_nla, sizeof(from_nla)); + recv_len = recvfrom(sk_nl, buff, BUFF_SIZE, 0, + (struct sockaddr*)&from_nla, &from_nla_len); + if (recv_len == ENOBUFS) { + flog(LOG_ERR, "ERROR: NETLINK BUFFER FULL, MESSAGE " + "DROPPED!"); + continue; + } + if (recv_len < 1) + continue; + while (NLMSG_OK(nlh, recv_len)) { + cn_hdr = NLMSG_DATA(nlh); + if (nlh->nlmsg_type == NLMSG_NOOP) + continue; + if ((nlh->nlmsg_type == NLMSG_ERROR) || + (nlh->nlmsg_type == NLMSG_OVERRUN)) + break; + if(cgre_handle_msg(cn_hdr) < 0) { + goto close_and_exit; + } + if (nlh->nlmsg_type == NLMSG_DONE) + break; + nlh = NLMSG_NEXT(nlh, recv_len); + } + } + +close_and_exit: + close(sk_nl); + return rc; +} + +/** + * Start logging. Opens syslog and/or log file and sets log level. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, NULL if no facility was specified + * @param logv Log verbosity, 2 is the default, 0 = no logging, 4 = everything + */ +static void cgre_start_log(const char *logp, int logf, int logv) +{ + /* Current system time */ + time_t tm; + + /* Log levels */ + int loglevels[] = { + LOG_EMERG, /* -qq */ + LOG_ERR, /* -q */ + LOG_NOTICE, /* default */ + LOG_INFO, /* -v */ + LOG_DEBUG /* -vv */ + }; + + /* Set default logging destination if nothing was specified */ + if (!logp && !logf) + logf = LOG_DAEMON; + + /* Open log file */ + if (logp) { + if (strcmp("-", logp) == 0) { + logfile = stdout; + } else { + logfile = fopen(logp, "a"); + if (!logfile) { + fprintf(stderr, "Failed to open log file %s," + " error: %s. Continuing anyway.\n", + logp, strerror(errno)); + logfile = stdout; + } + } + } else + logfile = NULL; + + /* Open syslog */ + if (logf) { + openlog("CGRE", LOG_CONS | LOG_PID, logf); + logfacility = logf; + } else + logfacility = 0; + + /* Set the log level */ + if (logv < 0) + logv = 0; + if (logv >= sizeof(loglevels)/sizeof(int)) + logv = sizeof(loglevels)/sizeof(int)-1; + + loglevel = loglevels[logv]; + + flog(LOG_DEBUG, "CGroup Rules Engine Daemon log started"); + tm = time(0); + flog(LOG_DEBUG, "Current time: %s", ctime(&tm)); + flog(LOG_DEBUG, "Opened log file: %s, log facility: %d, log level: %d", + logp, logfacility, loglevel); +} + + +/** + * Turns this program into a daemon. In doing so, we fork() and kill the + * parent process. Note too that stdout, stdin, and stderr are closed in + * daemon mode, and a file descriptor for a log file is opened. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, 0 if no facility was specified + * @param daemon False to turn off daemon mode (no fork, leave FDs open) + * @param logv Log verbosity, 2 is the default, 0 = no logging, 5 = everything + * @return 0 on success, > 0 on error + */ +int cgre_start_daemon(const char *logp, const int logf, + const unsigned char daemon, const int logv) +{ + /* PID returned from the fork() */ + pid_t pid; + + /* Fork and die. */ + if (daemon) { + pid = fork(); + if (pid < 0) { + openlog("CGRE", LOG_CONS, LOG_DAEMON|LOG_WARNING); + syslog(LOG_DAEMON|LOG_WARNING, "Failed to fork," + " error: %s", strerror(errno)); + closelog(); + fprintf(stderr, "Failed to fork(), %s\n", + strerror(errno)); + return 1; + } else if (pid > 0) { + exit(EXIT_SUCCESS); + } + + /* Change the file mode mask. */ + umask(0); + } else { + cgroup_dbg("Not using daemon mode.\n"); + pid = getpid(); + } + + cgre_start_log(logp, logf, logv); + + if (!daemon) { + /* We can skip the rest, since we're not becoming a daemon. */ + flog(LOG_INFO, "Proceeding with PID %d", getpid()); + return 0; + } else { + /* Get a new SID for the child. */ + if (setsid() < 0) { + flog(LOG_ERR, "Failed to get a new SID, error: %s", + strerror(errno)); + return 2; + } + + /* Change to the root directory. */ + if (chdir("/") < 0) { + flog(LOG_ERR, "Failed to chdir to /, error: %s", + strerror(errno)); + return 3; + } + + /* Close standard file descriptors. */ + close(STDIN_FILENO); + if (logfile != stdout) + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + /* If we make it this far, we're a real daemon! Or we chose not to. */ + flog(LOG_INFO, "Proceeding with PID %d", getpid()); + return 0; +} + +/** + * Catch the SIGUSR2 signal and reload the rules configuration. This function + * makes use of the logfile and flog() to print the new rules. + * @param signum The signal that we caught (always SIGUSR2) + */ +void cgre_flash_rules(int signum) +{ + /* Current time */ + time_t tm = time(0); + + flog(LOG_NOTICE, "Reloading rules configuration."); + flog(LOG_DEBUG, "Current time: %s", ctime(&tm)); + + /* Ask libcgroup to reload the rules table. */ + cgroup_reload_cached_rules(); + + /* Print the results of the new table to our log file. */ + if (logfile && loglevel >= LOG_INFO) { + cgroup_print_rules_config(logfile); + fprintf(logfile, "\n"); + } +} + +/** + * Catch the SIGTERM and SIGINT signals so that we can exit gracefully. Before + * exiting, this function makes use of the logfile and flog(). + * @param signum The signal that we caught (SIGTERM, SIGINT) + */ +void cgre_catch_term(int signum) +{ + /* Current time */ + time_t tm = time(0); + + flog(LOG_NOTICE, "Stopped CGroup Rules Engine Daemon at %s", + ctime(&tm)); + + /* Close the log file, if we opened one */ + if (logfile && logfile != stdout) + fclose(logfile); + + /* Close syslog */ + if (logfacility) + closelog(); + + exit(EXIT_SUCCESS); +} + +/** + * Parse the syslog facility as received on command line. + * @param arg Command line argument with the syslog facility + * @return the syslog facility (e.g. LOG_DAEMON) or 0 on error + */ +static int cgre_parse_syslog_facility(const char *arg) +{ + if (arg == NULL) + return 0; + + if (strlen(arg) > 1) + return 0; + + switch (arg[0]) { + case '0': + return LOG_LOCAL0; + case '1': + return LOG_LOCAL1; + case '2': + return LOG_LOCAL2; + case '3': + return LOG_LOCAL3; + case '4': + return LOG_LOCAL4; + case '5': + return LOG_LOCAL5; + case '6': + return LOG_LOCAL6; + case '7': + return LOG_LOCAL7; + case 'D': + return LOG_DAEMON; + default: + return 0; + } +} + +int main(int argc, char *argv[]) +{ + /* Patch to the log file */ + const char *logp = NULL; + + /* Syslog facility */ + int facility = 0; + + /* Verbose level */ + int verbosity = 2; + + /* For catching signals */ + struct sigaction sa; + + /* Should we daemonize? */ + unsigned char daemon = 1; + + /* Return codes */ + int ret = 0; + + /* Command line arguments */ + const char *short_options = "hvqf:s::ndQ"; + struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"quiet", no_argument, NULL, 'q'}, + {"logfile", required_argument, NULL, 'f'}, + {"syslog", optional_argument, NULL, 's'}, + {"nodaemon", no_argument, NULL, 'n'}, + {"debug", no_argument, NULL, 'd'}, + {"nolog", no_argument, NULL, 'Q'}, + {NULL, 0, NULL, 0} + }; + + /* Make sure the user is root. */ + if (getuid() != 0) { + fprintf(stderr, "Error: Only root can start/stop the control" + " group rules engine daemon\n"); + ret = 1; + goto finished; + } + + while (1) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) + break; + + switch (c) { + case 'h': /* --help */ + usage(stdout, "Help:\n"); + ret = 0; + goto finished; + + case 'v': /* --verbose */ + verbosity++; + break; + + case 'q': /* --quiet */ + verbosity--; + break; + + case 'Q': /* --nolog */ + verbosity = 0; + break; + + case 'f': /* --logfile=<filename> */ + logp = optarg; + break; + + case 's': /* --syslog=[facility] */ + if (optarg) { + facility = cgre_parse_syslog_facility(optarg); + if (facility == 0) { + fprintf(stderr, + "Unknown syslog facility: %s\n", + optarg); + ret = 2; + goto finished; + } + } else { + facility = LOG_DAEMON; + } + break; + + case 'n': /* --no-fork */ + daemon = 0; + break; + + case 'd': /* --debug */ + /* same as -vvn */ + daemon = 0; + verbosity = 4; + logp = "-"; + break; + + default: + usage(stderr, ""); + ret = 2; + goto finished; + } + } + + /* Initialize libcgroup. */ + if ((ret = cgroup_init()) != 0) { + fprintf(stderr, "Error: libcgroup initialization failed, %d\n", + ret); + goto finished; + } + + /* Ask libcgroup to load the configuration rules. */ + if ((ret = cgroup_init_rules_cache()) != 0) { + fprintf(stderr, "Error: libcgroup failed to initialize rules" + "cache, %d\n", ret); + goto finished; + } + + /* Now, start the daemon. */ + ret = cgre_start_daemon(logp, facility, daemon, verbosity); + if (ret < 0) { + fprintf(stderr, "Error: Failed to launch the daemon, %d\n", + ret); + goto finished; + } + + /* + * Set up the signal handler to reload the cached rules upon reception + * of a SIGUSR2 signal. + */ + sa.sa_handler = &cgre_flash_rules; + sa.sa_flags = 0; + sa.sa_restorer = NULL; + sigemptyset(&sa.sa_mask); + if ((ret = sigaction(SIGUSR2, &sa, NULL))) { + flog(LOG_ERR, "Failed to set up signal handler for SIGUSR2." + " Error: %s", strerror(errno)); + goto finished; + } + + /* + * Set up the signal handler to catch SIGINT and SIGTERM so that we + * can exit gracefully. + */ + sa.sa_handler = &cgre_catch_term; + ret = sigaction(SIGINT, &sa, NULL); + ret |= sigaction(SIGTERM, &sa, NULL); + if (ret) { + flog(LOG_ERR, "Failed to set up the signal handler. Error:" + " %s", strerror(errno)); + goto finished; + } + + /* Print the configuration to the log file, or stdout. */ + if (logfile && loglevel >= LOG_INFO) + cgroup_print_rules_config(logfile); + + flog(LOG_NOTICE, "Started the CGroup Rules Engine Daemon."); + + /* We loop endlesly in this function, unless we encounter an error. */ + ret = cgre_create_netlink_socket_process_msg(); + +finished: + if (logfile && logfile != stdout) + fclose(logfile); + + return ret; +} diff --git a/src/daemon/cgrulesengd.h b/src/daemon/cgrulesengd.h new file mode 100644 index 0000000..1840143 --- /dev/null +++ b/src/daemon/cgrulesengd.h @@ -0,0 +1,127 @@ +/* + * Copyright Red Hat Inc. 2008 + * + * Author: Steve Olivieri <sjo@redhat.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _CGRULESENGD_H +#define _CGRULESENGD_H + +#include <features.h> + +__BEGIN_DECLS + +#include "config.h" +#include "libcgroup.h" +#include <linux/connector.h> + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef __USE_GNU +#define __USE_GNU +#endif + +/* A simple macro for printing messages only when CGROUP_DEBUG is defined. */ +#ifdef CGROUP_DEBUG + #define cgroup_dbg(a...) printf(a) +#else + #define cgroup_dbg(a...) do {} while (0) +#endif /* CGROUP_DEBUG */ + +/* The following ten macros are all for the Netlink code. */ +#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ + sizeof(enum proc_cn_mcast_op))) +#define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \ + sizeof(struct proc_event))) + +#define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN)) +#define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN)) + +#define max(x,y) ((y)<(x)?(x):(y)) +#define min(x,y) ((y)>(x)?(x):(y)) + +#define BUFF_SIZE (max(max(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024)) +#define MIN_RECV_SIZE (min(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE)) + +#define PROC_CN_MCAST_LISTEN (1) +#define PROC_CN_MCAST_IGNORE (2) + +/** + * Prints the usage information for this program and, optionally, an error + * message. This function uses vfprintf. + * @param fd The file stream to print to + * @param msg The error message to print (printf style) + * @param ... Any args to msg (printf style) + */ +void cgre_usage(FILE *fd, const char *msg, ...); + +/** + * Prints a formatted message (like printf()) to all log destinations. + * Flushes the file stream's buffer so that the message is immediately + * readable. + * @param level The log level (LOG_EMERG ... LOG_DEBUG) + * @param format The format for the message (printf style) + * @param ... Any args to format (printf style) + */ +void flog(int level, const char *msg, ...); + +/** + * Process an event from the kernel, and determine the correct UID/GID/PID to + * pass to libcgroup. Then, libcgroup will decide the cgroup to move the PID + * to, if any. + * @param ev The event to process + * @param type The type of event to process (part of ev) + * @return 0 on success, > 0 on failure + */ +int cgre_process_event(const struct proc_event *ev, const int type); + +/** + * Handle a netlink message. In the event of PROC_EVENT_UID or PROC_EVENT_GID, + * we pass the event along to cgre_process_event for further processing. All + * other events are ignored. + * @param cn_hdr The netlink message + * @return 0 on success, > 0 on error + */ +int cgre_handle_message(struct cn_msg *cn_hdr); + +/** + * Turns this program into a daemon. In doing so, we fork() and kill the + * parent process. Note too that stdout, stdin, and stderr are closed in + * daemon mode, and a file descriptor for a log file is opened. + * @param logp Path of the log file, NULL if no log file was specified + * @param logf Syslog facility, NULL if no facility was specified + * @param daemon False to turn off daemon mode (no fork, leave FDs open) + * @param logv Log verbosity, 2 is the default, 0 = no logging, 5 = everything + * @return 0 on success, > 0 on error + */ +int cgre_start_daemon(const char *logp, const int logf, + const unsigned char daemon, const int logv); + +/** + * Catch the SIGUSR2 signal and reload the rules configuration. This function + * makes use of the logfile and flog() to print the new rules. + * @param signum The signal that we caught (always SIGUSR2) + */ +void cgre_flash_rules(int signum); + +/** + * Catch the SIGTERM and SIGINT signal so that we can exit gracefully. Before + * exiting, this function makes use of the logfile and flog(). + * @param signum The signal that we caught (SIGTERM, SIGINT) + */ +void cgre_catch_term(int signum); + +__END_DECLS + +#endif /* _CGRULESENGD_H */ + |