~ubuntu-branches/ubuntu/maverick/libcgroup/maverick-proposed

« back to all changes in this revision

Viewing changes to src/daemon/cgrulesengd.c

  • Committer: Bazaar Package Importer
  • Author(s): Dustin Kirkland
  • Date: 2009-08-26 11:29:17 UTC
  • Revision ID: james.westby@ubuntu.com-20090826112917-402ews2uj6v350d2
Tags: upstream-0.34
ImportĀ upstreamĀ versionĀ 0.34

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright Red Hat Inc. 2008
 
3
 *
 
4
 * Author: Steve Olivieri <sjo@redhat.com>
 
5
 * Author: Vivek Goyal <vgoyal@redhat.com>
 
6
 *
 
7
 * Some part of the programs have been derived from Dhaval Giani's posting
 
8
 * for daemon to place the task in right container. Original copyright notice
 
9
 * follows.
 
10
 *
 
11
 * Copyright IBM Corporation, 2007
 
12
 * Author: Dhaval Giani <dhaval <at> linux.vnet.ibm.com>
 
13
 * Derived from test_cn_proc.c by Matt Helsley
 
14
 * Original copyright notice follows
 
15
 *
 
16
 * Copyright (C) Matt Helsley, IBM Corp. 2005
 
17
 * Derived from fcctl.c by Guillaume Thouvenin
 
18
 * Original copyright notice follows:
 
19
 *
 
20
 * Copyright (C) 2005 BULL SA.
 
21
 * Written by Guillaume Thouvenin <guillaume.thouvenin <at> bull.net>
 
22
 *
 
23
 * This program is free software; you can redistribute it and/or modify it
 
24
 * under the terms of version 2.1 of the GNU Lesser General Public License
 
25
 * as published by the Free Software Foundation.
 
26
 *
 
27
 * This program is distributed in the hope that it would be useful, but
 
28
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
29
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
30
 *
 
31
 * TODO Stop using netlink for communication (or at least rewrite that part).
 
32
 */
 
33
 
 
34
#include "libcgroup.h"
 
35
#include "cgrulesengd.h"
 
36
#include "../libcgroup-internal.h"
 
37
 
 
38
#include <errno.h>
 
39
#include <stdarg.h>
 
40
#include <stdio.h>
 
41
#include <stdlib.h>
 
42
#include <sys/socket.h>
 
43
#include <sys/syslog.h>
 
44
#include <string.h>
 
45
#include <linux/netlink.h>
 
46
#include <signal.h>
 
47
#include <time.h>
 
48
#include <syslog.h>
 
49
#include <getopt.h>
 
50
 
 
51
#include <sys/stat.h>
 
52
#include <sys/types.h>
 
53
#include <unistd.h>
 
54
#include <linux/connector.h>
 
55
#include <linux/cn_proc.h>
 
56
#include <linux/un.h>
 
57
 
 
58
#define NUM_PER_REALLOCATIOM    (100)
 
59
 
 
60
/* Log file, NULL if logging to file is disabled */
 
61
FILE* logfile;
 
62
 
 
63
/* Log facility, 0 if logging to syslog is disabled */
 
64
int logfacility;
 
65
 
 
66
/* Current log level */
 
67
int loglevel;
 
68
 
 
69
/**
 
70
 * Prints the usage information for this program and, optionally, an error
 
71
 * message.  This function uses vfprintf.
 
72
 *      @param fd The file stream to print to
 
73
 *      @param msg The error message to print (printf style)
 
74
 *      @param ... Any args to msg (printf style)
 
75
 */
 
76
void usage(FILE* fd, const char* msg, ...)
 
77
{
 
78
        /* List of args to msg */
 
79
        va_list ap;
 
80
 
 
81
        /* Put all args after msg into the list. */
 
82
        va_start(ap, msg);
 
83
 
 
84
        if (msg)
 
85
                vfprintf(fd, msg, ap);
 
86
        fprintf(fd, "\n");
 
87
        fprintf(fd, "cgrulesengd -- a daemon for the cgroups rules engine\n\n");
 
88
        fprintf(fd, "Usage : cgrulesengd [options]\n\n");
 
89
        fprintf(fd, "  options :\n");
 
90
        fprintf(fd, "    -q           | --quiet             quiet mode\n"
 
91
                "    -v           | --verbose           verbose mode\n"
 
92
                "    -f <path>    | --logfile=<path>    write log to file\n"
 
93
                "    -s[facility] | --syslog=[facility] write log to syslog\n"
 
94
                "    -n           | --nodaemom          don't fork daemon\n"
 
95
                "    -d           | --debug             same as -v -v -n -f -\n"
 
96
                "    -Q           | --nolog             disable logging\n"
 
97
                "    -h           | --help              show this help\n\n"
 
98
                );
 
99
        va_end(ap);
 
100
}
 
101
 
 
102
/**
 
103
 * Prints a formatted message (like printf()) to all log destinations.
 
104
 * Flushes the file stream's buffer so that the message is immediately
 
105
 * readable.
 
106
 *      @param level The log level (LOG_EMERG ... LOG_DEBUG)
 
107
 *      @param format The format for the message (printf style)
 
108
 *      @param ... Any args to format (printf style)
 
109
 */
 
110
void flog(int level, const char *format, ...)
 
111
{
 
112
        /* List of args to format */
 
113
        va_list ap;
 
114
 
 
115
        /* Check the log level */
 
116
        if (level > loglevel)
 
117
                return;
 
118
 
 
119
        if (logfile) {
 
120
                /* Print the message to the given stream. */
 
121
                va_start(ap, format);
 
122
                vfprintf(logfile, format, ap);
 
123
                va_end(ap);
 
124
                fprintf(logfile, "\n");
 
125
 
 
126
                /*
 
127
                 * Flush the stream's buffer, so the data is readable
 
128
                 * immediately.
 
129
                 */
 
130
                fflush(logfile);
 
131
        }
 
132
 
 
133
        if (logfacility) {
 
134
                sigset_t sigset;
 
135
 
 
136
                sigemptyset(&sigset);
 
137
                sigaddset(&sigset, SIGUSR2);
 
138
                sigprocmask(SIG_BLOCK, &sigset, NULL);
 
139
 
 
140
                va_start(ap, format);
 
141
                vsyslog(LOG_MAKEPRI(logfacility, level), format, ap);
 
142
                va_end(ap);
 
143
 
 
144
                sigprocmask(SIG_UNBLOCK, &sigset, NULL);
 
145
        }
 
146
}
 
147
 
 
148
struct parent_info {
 
149
        __u64 timestamp;
 
150
        pid_t pid;
 
151
};
 
152
struct array_parent_info {
 
153
        int index;
 
154
        int num_allocation;
 
155
        struct parent_info **parent_info;
 
156
};
 
157
struct array_parent_info array_pi;
 
158
 
 
159
static int cgre_store_parent_info(pid_t pid)
 
160
{
 
161
        __u64 uptime_ns;
 
162
        struct timespec tp;
 
163
        struct parent_info *info;
 
164
 
 
165
        if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) {
 
166
                flog(LOG_WARNING, "Failed to get time");
 
167
                return 1;
 
168
        }
 
169
        uptime_ns = ((__u64)tp.tv_sec * 1000 * 1000 * 1000 ) + tp.tv_nsec;
 
170
 
 
171
        if (array_pi.index >= array_pi.num_allocation) {
 
172
                array_pi.num_allocation += NUM_PER_REALLOCATIOM;
 
173
                array_pi.parent_info = realloc(array_pi.parent_info,
 
174
                                        sizeof(info) * array_pi.num_allocation);
 
175
                if (!array_pi.parent_info) {
 
176
                        flog(LOG_WARNING, "Failed to allocate memory");
 
177
                        return 1;
 
178
                }
 
179
        }
 
180
        info = calloc(1, sizeof(struct parent_info));
 
181
        if (!info) {
 
182
                flog(LOG_WARNING, "Failed to allocate memory");
 
183
                return 1;
 
184
        }
 
185
        info->timestamp = uptime_ns;
 
186
        info->pid = pid;
 
187
 
 
188
        array_pi.parent_info[array_pi.index] = info;
 
189
        array_pi.index++;
 
190
 
 
191
        return 0;
 
192
}
 
193
 
 
194
static void cgre_remove_old_parent_info(__u64 key_timestamp)
 
195
{
 
196
        int i, j;
 
197
 
 
198
        for (i = 0; i < array_pi.index; i++) {
 
199
                if (key_timestamp < array_pi.parent_info[i]->timestamp)
 
200
                        continue;
 
201
                free(array_pi.parent_info[i]);
 
202
                for (j = i; j < array_pi.index - 1; j++)
 
203
                        array_pi.parent_info[j] = array_pi.parent_info[j + 1];
 
204
                array_pi.index--;
 
205
                i--;
 
206
        }
 
207
        return;
 
208
}
 
209
 
 
210
static int cgre_was_parent_changed_when_forking(const struct proc_event *ev)
 
211
{
 
212
        int i;
 
213
        pid_t parent_pid;
 
214
        __u64 timestamp_child;
 
215
        __u64 timestamp_parent;
 
216
 
 
217
        parent_pid = ev->event_data.fork.parent_pid;
 
218
        timestamp_child = ev->timestamp_ns;
 
219
 
 
220
        cgre_remove_old_parent_info(timestamp_child);
 
221
 
 
222
        for (i = 0; i < array_pi.index; i++) {
 
223
                if (parent_pid != array_pi.parent_info[i]->pid)
 
224
                        continue;
 
225
                timestamp_parent = array_pi.parent_info[i]->timestamp;
 
226
                if (timestamp_child > timestamp_parent)
 
227
                        continue;
 
228
                return 1;
 
229
        }
 
230
        return 0;
 
231
}
 
232
 
 
233
struct unchanged_pid {
 
234
        pid_t pid;
 
235
        int flags;
 
236
} unchanged_pid_t;
 
237
 
 
238
struct array_unchanged {
 
239
        int index;
 
240
        int num_allocation;
 
241
        struct unchanged_pid *proc;
 
242
};
 
243
 
 
244
struct array_unchanged array_unch;
 
245
 
 
246
static int cgre_store_unchanged_process(pid_t pid, int flags)
 
247
{
 
248
        int i;
 
249
 
 
250
        for (i = 0; i < array_unch.index; i++) {
 
251
                if (array_unch.proc[i].pid != pid)
 
252
                        continue;
 
253
                /* pid is stored already. */
 
254
                return 0;
 
255
        }
 
256
        if (array_unch.index >= array_unch.num_allocation) {
 
257
                array_unch.num_allocation += NUM_PER_REALLOCATIOM;
 
258
                array_unch.proc = realloc(array_unch.proc,
 
259
                        sizeof(unchanged_pid_t) * array_unch.num_allocation);
 
260
                if (!array_unch.proc) {
 
261
                        flog(LOG_WARNING, "Failed to allocate memory");
 
262
                        return 1;
 
263
                }
 
264
        }
 
265
        array_unch.proc[array_unch.index].pid = pid;
 
266
        array_unch.proc[array_unch.index].flags = flags;
 
267
        array_unch.index++;
 
268
        flog(LOG_DEBUG, "Store the unchanged process (PID: %d, FLAGS: %d)",
 
269
                        pid, flags);
 
270
        return 0;
 
271
}
 
272
 
 
273
static void cgre_remove_unchanged_process(pid_t pid)
 
274
{
 
275
        int i, j;
 
276
 
 
277
        for (i = 0; i < array_unch.index; i++) {
 
278
                if (array_unch.proc[i].pid != pid)
 
279
                        continue;
 
280
                for (j = i; j < array_unch.index - 1; j++)
 
281
                        memcpy(&array_unch.proc[j],
 
282
                                &array_unch.proc[j + 1],
 
283
                                sizeof(struct unchanged_pid));
 
284
                array_unch.index--;
 
285
                flog(LOG_DEBUG, "Remove the unchanged process (PID: %d)", pid);
 
286
                break;
 
287
        }
 
288
        return;
 
289
}
 
290
 
 
291
static int cgre_is_unchanged_process(pid_t pid)
 
292
{
 
293
        int i;
 
294
 
 
295
        for (i = 0; i < array_unch.index; i++) {
 
296
                if (array_unch.proc[i].pid != pid)
 
297
                        continue;
 
298
                return 1;
 
299
        }
 
300
        return 0;
 
301
}
 
302
 
 
303
static int cgre_is_unchanged_child(pid_t pid)
 
304
{
 
305
        int i;
 
306
 
 
307
        for (i = 0; i < array_unch.index; i++) {
 
308
                if (array_unch.proc[i].pid != pid)
 
309
                        continue;
 
310
                if (array_unch.proc[i].flags & CGROUP_DAEMON_UNCHANGE_CHILDREN)
 
311
                        return 1;
 
312
                break;
 
313
        }
 
314
        return 0;
 
315
}
 
316
 
 
317
static int cgre_change_cgroup(const uid_t uid, const gid_t gid, char *procname,
 
318
                                        const pid_t pid)
 
319
{
 
320
        int ret;
 
321
        sigset_t sigset;
 
322
 
 
323
        /*
 
324
         * For avoiding the deadlock, protect cdgroup_change_cgroup_
 
325
         * ~uid_gid_flags() by blocking SIGUSR2 signal.
 
326
         */
 
327
        sigemptyset(&sigset);
 
328
        sigaddset(&sigset, SIGUSR2);
 
329
        sigprocmask(SIG_BLOCK, &sigset, NULL);
 
330
 
 
331
        ret = cgroup_change_cgroup_flags(uid, gid, procname, pid,
 
332
                                                 CGFLAG_USECACHE);
 
333
        sigprocmask(SIG_UNBLOCK, &sigset, NULL);
 
334
 
 
335
        return ret;
 
336
}
 
337
 
 
338
/**
 
339
 * Process an event from the kernel, and determine the correct UID/GID/PID to
 
340
 * pass to libcgroup.  Then, libcgroup will decide the cgroup to move the PID
 
341
 * to, if any.
 
342
 *      @param ev The event to process
 
343
 *      @param type The type of event to process (part of ev)
 
344
 *      @return 0 on success, > 0 on failure
 
345
 */
 
346
int cgre_process_event(const struct proc_event *ev, const int type)
 
347
{
 
348
        char *procname;
 
349
        pid_t ppid, cpid;
 
350
        pid_t pid = 0, log_pid = 0;
 
351
        uid_t euid, log_uid = 0;
 
352
        gid_t egid, log_gid = 0;
 
353
 
 
354
        int ret = 0;
 
355
 
 
356
        switch (type) {
 
357
        case PROC_EVENT_UID:
 
358
        case PROC_EVENT_GID:
 
359
                pid = ev->event_data.id.process_pid;
 
360
                break;
 
361
        case PROC_EVENT_FORK:
 
362
                ppid = ev->event_data.fork.parent_pid;
 
363
                cpid = ev->event_data.fork.child_pid;
 
364
                if (cgre_is_unchanged_child(ppid)) {
 
365
                        if (cgre_store_unchanged_process(cpid,
 
366
                                        CGROUP_DAEMON_UNCHANGE_CHILDREN))
 
367
                                return 1;
 
368
                }
 
369
 
 
370
                /*
 
371
                 * If this process was forked while changing parent's cgroup,
 
372
                 * this process's cgroup also should be changed.
 
373
                 */
 
374
                if (!cgre_was_parent_changed_when_forking(ev))
 
375
                        return 0;
 
376
                pid = ev->event_data.fork.child_pid;
 
377
                break;
 
378
        case PROC_EVENT_EXIT:
 
379
                cgre_remove_unchanged_process(ev->event_data.exit.process_pid);
 
380
                return 0;
 
381
        case PROC_EVENT_EXEC:
 
382
                /*
 
383
                 * If the unchanged process, the daemon should not change the
 
384
                 * cgroup of the process.
 
385
                 */
 
386
                if (cgre_is_unchanged_process(ev->event_data.exec.process_pid))
 
387
                        return 0;
 
388
                pid = ev->event_data.exec.process_pid;
 
389
                break;
 
390
        default:
 
391
                break;
 
392
        }
 
393
        ret = cgroup_get_uid_gid_from_procfs(pid, &euid, &egid);
 
394
        if (ret == ECGROUPNOTEXIST)
 
395
                /* cgroup_get_uid_gid_from_procfs() returns ECGROUPNOTEXIST
 
396
                 * if a process finished and that is not a problem. */
 
397
                return 0;
 
398
        else if (ret)
 
399
                return ret;
 
400
 
 
401
        ret = cgroup_get_procname_from_procfs(pid, &procname);
 
402
        if (ret == ECGROUPNOTEXIST)
 
403
                return 0;
 
404
        else if (ret)
 
405
                return ret;
 
406
 
 
407
        /*
 
408
         * Now that we have the UID, the GID, and the PID, we can make a call
 
409
         * to libcgroup to change the cgroup for this PID.
 
410
         */
 
411
        log_pid = pid;
 
412
        switch (type) {
 
413
        case PROC_EVENT_UID:
 
414
                log_uid = ev->event_data.id.e.euid;
 
415
                log_gid = egid;
 
416
                euid = ev->event_data.id.e.euid;
 
417
                break;
 
418
        case PROC_EVENT_GID:
 
419
                log_uid = euid;
 
420
                log_gid = ev->event_data.id.e.egid;
 
421
                egid = ev->event_data.id.e.egid;
 
422
                break;
 
423
        case PROC_EVENT_FORK:
 
424
                log_uid = euid;
 
425
                log_gid = egid;
 
426
                break;
 
427
        case PROC_EVENT_EXEC:
 
428
                log_uid = euid;
 
429
                log_gid = egid;
 
430
                break;
 
431
        default:
 
432
                break;
 
433
        }
 
434
        ret = cgre_change_cgroup(euid, egid, procname, pid);
 
435
        if (ret) {
 
436
                /*
 
437
                 * TODO: add some supression, do not spam log when every group
 
438
                 * change fails
 
439
                 */
 
440
                flog(LOG_WARNING, "Cgroup change for PID: %d, UID: %d, GID: %d"
 
441
                        " FAILED! (Error Code: %d)", log_pid, log_uid, log_gid,
 
442
                        ret);
 
443
        } else {
 
444
                ret = cgre_store_parent_info(pid);
 
445
                flog(LOG_INFO, "Cgroup change for PID: %d, UID: %d, GID: %d OK",
 
446
                        log_pid, log_uid, log_gid);
 
447
        }
 
448
        free(procname);
 
449
        return ret;
 
450
}
 
451
 
 
452
/**
 
453
 * Handle a netlink message.  In the event of PROC_EVENT_UID or PROC_EVENT_GID,
 
454
 * we pass the event along to cgre_process_event for further processing.  All
 
455
 * other events are ignored.
 
456
 *      @param cn_hdr The netlink message
 
457
 *      @return 0 on success, > 0 on error
 
458
 */
 
459
int cgre_handle_msg(struct cn_msg *cn_hdr)
 
460
{
 
461
        /* The event to consider */
 
462
        struct proc_event *ev;
 
463
 
 
464
        /* Return codes */
 
465
        int ret = 0;
 
466
 
 
467
        /* Get the event data.  We only care about two event types. */
 
468
        ev = (struct proc_event*)cn_hdr->data;
 
469
        switch (ev->what) {
 
470
        case PROC_EVENT_UID:
 
471
                flog(LOG_DEBUG, "UID Event: PID = %d, tGID = %d, rUID = %d,"
 
472
                                " eUID = %d", ev->event_data.id.process_pid,
 
473
                                ev->event_data.id.process_tgid,
 
474
                                ev->event_data.id.r.ruid,
 
475
                                ev->event_data.id.e.euid);
 
476
                ret = cgre_process_event(ev, PROC_EVENT_UID);
 
477
                break;
 
478
        case PROC_EVENT_GID:
 
479
                flog(LOG_DEBUG, "GID Event: PID = %d, tGID = %d, rGID = %d,"
 
480
                                " eGID = %d", ev->event_data.id.process_pid,
 
481
                                ev->event_data.id.process_tgid,
 
482
                                ev->event_data.id.r.rgid,
 
483
                                ev->event_data.id.e.egid);
 
484
                ret = cgre_process_event(ev, PROC_EVENT_GID);
 
485
                break;
 
486
        case PROC_EVENT_FORK:
 
487
                ret = cgre_process_event(ev, PROC_EVENT_FORK);
 
488
                break;
 
489
        case PROC_EVENT_EXIT:
 
490
                ret = cgre_process_event(ev, PROC_EVENT_EXIT);
 
491
                break;
 
492
        case PROC_EVENT_EXEC:
 
493
                flog(LOG_DEBUG, "EXEC Event: PID = %d, tGID = %d",
 
494
                                ev->event_data.exec.process_pid,
 
495
                                ev->event_data.exec.process_tgid);
 
496
                ret = cgre_process_event(ev, PROC_EVENT_EXEC);
 
497
                break;
 
498
        default:
 
499
                break;
 
500
        }
 
501
 
 
502
        return ret;
 
503
}
 
504
 
 
505
int cgre_receive_netlink_msg(int sk_nl)
 
506
{
 
507
        char buff[BUFF_SIZE];
 
508
        size_t recv_len;
 
509
        struct sockaddr_nl from_nla;
 
510
        socklen_t from_nla_len;
 
511
        struct nlmsghdr *nlh;
 
512
        struct sockaddr_nl kern_nla;
 
513
        struct cn_msg *cn_hdr;
 
514
 
 
515
        kern_nla.nl_family = AF_NETLINK;
 
516
        kern_nla.nl_groups = CN_IDX_PROC;
 
517
        kern_nla.nl_pid = 1;
 
518
        kern_nla.nl_pad = 0;
 
519
 
 
520
        memset(buff, 0, sizeof(buff));
 
521
        from_nla_len = sizeof(from_nla);
 
522
        memcpy(&from_nla, &kern_nla, sizeof(from_nla));
 
523
        recv_len = recvfrom(sk_nl, buff, sizeof(buff), 0,
 
524
                (struct sockaddr *)&from_nla, &from_nla_len);
 
525
        if (recv_len == ENOBUFS) {
 
526
                flog(LOG_ERR, "ERROR: NETLINK BUFFER FULL, MESSAGE DROPPED!");
 
527
                return 0;
 
528
        }
 
529
        if (recv_len < 1)
 
530
                return 0;
 
531
 
 
532
        nlh = (struct nlmsghdr *)buff;
 
533
        while (NLMSG_OK(nlh, recv_len)) {
 
534
                cn_hdr = NLMSG_DATA(nlh);
 
535
                if (nlh->nlmsg_type == NLMSG_NOOP) {
 
536
                        nlh = NLMSG_NEXT(nlh, recv_len);
 
537
                        continue;
 
538
                }
 
539
                if ((nlh->nlmsg_type == NLMSG_ERROR) ||
 
540
                                (nlh->nlmsg_type == NLMSG_OVERRUN))
 
541
                        break;
 
542
                if (cgre_handle_msg(cn_hdr) < 0)
 
543
                        return 1;
 
544
                if (nlh->nlmsg_type == NLMSG_DONE)
 
545
                        break;
 
546
                nlh = NLMSG_NEXT(nlh, recv_len);
 
547
        }
 
548
        return 0;
 
549
}
 
550
 
 
551
void cgre_receive_unix_domain_msg(int sk_unix)
 
552
{
 
553
        int flags;
 
554
        int fd_client;
 
555
        pid_t pid;
 
556
        struct sockaddr_un caddr;
 
557
        socklen_t caddr_len;
 
558
        struct stat buff_stat;
 
559
        char path[FILENAME_MAX];
 
560
 
 
561
        caddr_len = sizeof(caddr);
 
562
        fd_client = accept(sk_unix, (struct sockaddr *)&caddr, &caddr_len);
 
563
        if (fd_client < 0) {
 
564
                cgroup_dbg("accept error");
 
565
                return;
 
566
        }
 
567
        if (read(fd_client, &pid, sizeof(pid)) < 0) {
 
568
                cgroup_dbg("read error");
 
569
                goto close;
 
570
        }
 
571
        sprintf(path, "/proc/%d", pid);
 
572
        if (stat(path, &buff_stat)) {
 
573
                cgroup_dbg("There is not such process (PID: %d)", pid);
 
574
                goto close;
 
575
        }
 
576
        if (read(fd_client, &flags, sizeof(flags)) < 0) {
 
577
                cgroup_dbg("read error");
 
578
                goto close;
 
579
        }
 
580
        if (cgre_store_unchanged_process(pid, flags))
 
581
                goto close;
 
582
 
 
583
        if (write(fd_client, CGRULE_SUCCESS_STORE_PID,
 
584
                        sizeof(CGRULE_SUCCESS_STORE_PID)) < 0) {
 
585
                cgroup_dbg("write error");
 
586
                goto close;
 
587
        }
 
588
close:
 
589
        close(fd_client);
 
590
        return;
 
591
}
 
592
 
 
593
int cgre_create_netlink_socket_process_msg()
 
594
{
 
595
        int sk_nl = 0, sk_unix = 0, sk_max;
 
596
        struct sockaddr_nl my_nla;
 
597
        char buff[BUFF_SIZE];
 
598
        int rc = -1;
 
599
        struct nlmsghdr *nl_hdr;
 
600
        struct cn_msg *cn_hdr;
 
601
        enum proc_cn_mcast_op *mcop_msg;
 
602
        struct sockaddr_un saddr;
 
603
        fd_set fds, readfds;
 
604
 
 
605
        /*
 
606
         * Create an endpoint for communication. Use the kernel user
 
607
         * interface device (PF_NETLINK) which is a datagram oriented
 
608
         * service (SOCK_DGRAM). The protocol used is the connector
 
609
         * protocol (NETLINK_CONNECTOR)
 
610
         */
 
611
        sk_nl = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
 
612
        if (sk_nl == -1) {
 
613
                cgroup_dbg("socket sk_nl error");
 
614
                return rc;
 
615
        }
 
616
 
 
617
        my_nla.nl_family = AF_NETLINK;
 
618
        my_nla.nl_groups = CN_IDX_PROC;
 
619
        my_nla.nl_pid = getpid();
 
620
        my_nla.nl_pad = 0;
 
621
 
 
622
        if (bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla)) < 0) {
 
623
                cgroup_dbg("binding sk_nl error");
 
624
                goto close_and_exit;
 
625
        }
 
626
 
 
627
        nl_hdr = (struct nlmsghdr *)buff;
 
628
        cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr);
 
629
        mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0];
 
630
        cgroup_dbg("sending proc connector: PROC_CN_MCAST_LISTEN... ");
 
631
        memset(buff, 0, sizeof(buff));
 
632
        *mcop_msg = PROC_CN_MCAST_LISTEN;
 
633
 
 
634
        /* fill the netlink header */
 
635
        nl_hdr->nlmsg_len = SEND_MESSAGE_LEN;
 
636
        nl_hdr->nlmsg_type = NLMSG_DONE;
 
637
        nl_hdr->nlmsg_flags = 0;
 
638
        nl_hdr->nlmsg_seq = 0;
 
639
        nl_hdr->nlmsg_pid = getpid();
 
640
 
 
641
        /* fill the connector header */
 
642
        cn_hdr->id.idx = CN_IDX_PROC;
 
643
        cn_hdr->id.val = CN_VAL_PROC;
 
644
        cn_hdr->seq = 0;
 
645
        cn_hdr->ack = 0;
 
646
        cn_hdr->len = sizeof(enum proc_cn_mcast_op);
 
647
        cgroup_dbg("sending netlink message len=%d, cn_msg len=%d\n",
 
648
                nl_hdr->nlmsg_len, (int) sizeof(struct cn_msg));
 
649
        if (send(sk_nl, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
 
650
                cgroup_dbg("failed to send proc connector mcast ctl op!\n");
 
651
                goto close_and_exit;
 
652
        }
 
653
        cgroup_dbg("sent\n");
 
654
 
 
655
        /*
 
656
         * Setup Unix domain socket.
 
657
         */
 
658
        sk_unix = socket(PF_UNIX, SOCK_STREAM, 0);
 
659
        if (sk_unix < 0) {
 
660
                cgroup_dbg("socket sk_unix error");
 
661
                goto close_and_exit;
 
662
        }
 
663
        memset(&saddr, 0, sizeof(saddr));
 
664
        saddr.sun_family = AF_UNIX;
 
665
        strcpy(saddr.sun_path, CGRULE_CGRED_SOCKET_PATH);
 
666
        unlink(CGRULE_CGRED_SOCKET_PATH);
 
667
        if (bind(sk_unix, (struct sockaddr *)&saddr,
 
668
            sizeof(saddr.sun_family) + strlen(CGRULE_CGRED_SOCKET_PATH)) < 0) {
 
669
                cgroup_dbg("binding sk_unix error");
 
670
                goto close_and_exit;
 
671
        }
 
672
        if (listen(sk_unix, 1) < 0) {
 
673
                cgroup_dbg("listening sk_unix error");
 
674
                goto close_and_exit;
 
675
        }
 
676
        FD_ZERO(&readfds);
 
677
        FD_SET(sk_nl, &readfds);
 
678
        FD_SET(sk_unix, &readfds);
 
679
        if (sk_nl < sk_unix)
 
680
                sk_max = sk_unix;
 
681
        else
 
682
                sk_max = sk_nl;
 
683
        for(;;) {
 
684
                memcpy(&fds, &readfds, sizeof(fd_set));
 
685
                if (select(sk_max + 1, &fds, NULL, NULL, NULL) < 0) {
 
686
                        cgroup_dbg("selecting error");
 
687
                        goto close_and_exit;
 
688
                }
 
689
                if (FD_ISSET(sk_nl, &fds)) {
 
690
                        if (cgre_receive_netlink_msg(sk_nl))
 
691
                                break;
 
692
                }
 
693
                if (FD_ISSET(sk_unix, &fds))
 
694
                        cgre_receive_unix_domain_msg(sk_unix);
 
695
        }
 
696
 
 
697
close_and_exit:
 
698
        if (sk_nl)
 
699
                close(sk_nl);
 
700
        if (sk_unix)
 
701
                close(sk_unix);
 
702
        return rc;
 
703
}
 
704
 
 
705
/**
 
706
 * Start logging. Opens syslog and/or log file and sets log level.
 
707
 *      @param logp Path of the log file, NULL if no log file was specified
 
708
 *      @param logf Syslog facility, NULL if no facility was specified
 
709
 *      @param logv Log verbosity, 2 is the default, 0 = no logging, 4 = everything
 
710
 */
 
711
static void cgre_start_log(const char *logp, int logf, int logv)
 
712
{
 
713
        /* Current system time */
 
714
        time_t tm;
 
715
 
 
716
        /* Log levels */
 
717
        int loglevels[] = {
 
718
                LOG_EMERG,              /* -qq */
 
719
                LOG_ERR,                /* -q */
 
720
                LOG_NOTICE,             /* default */
 
721
                LOG_INFO,               /* -v */
 
722
                LOG_DEBUG               /* -vv */
 
723
        };
 
724
 
 
725
        /* Set default logging destination if nothing was specified */
 
726
        if (!logp && !logf)
 
727
                logf = LOG_DAEMON;
 
728
 
 
729
        /* Open log file */
 
730
        if (logp) {
 
731
                if (strcmp("-", logp) == 0) {
 
732
                        logfile = stdout;
 
733
                } else {
 
734
                        logfile = fopen(logp, "a");
 
735
                        if (!logfile) {
 
736
                                fprintf(stderr, "Failed to open log file %s,"
 
737
                                        " error: %s. Continuing anyway.\n",
 
738
                                        logp, strerror(errno));
 
739
                                logfile = stdout;
 
740
                        }
 
741
                }
 
742
        } else
 
743
                logfile = NULL;
 
744
 
 
745
        /* Open syslog */
 
746
        if (logf) {
 
747
                openlog("CGRE", LOG_CONS | LOG_PID, logf);
 
748
                logfacility = logf;
 
749
        } else
 
750
                logfacility = 0;
 
751
 
 
752
        /* Set the log level */
 
753
        if (logv < 0)
 
754
                logv = 0;
 
755
        if (logv >= sizeof(loglevels)/sizeof(int))
 
756
                logv = sizeof(loglevels)/sizeof(int)-1;
 
757
 
 
758
        loglevel = loglevels[logv];
 
759
 
 
760
        flog(LOG_DEBUG, "CGroup Rules Engine Daemon log started");
 
761
        tm = time(0);
 
762
        flog(LOG_DEBUG, "Current time: %s", ctime(&tm));
 
763
        flog(LOG_DEBUG, "Opened log file: %s, log facility: %d, log level: %d",
 
764
                        logp, logfacility, loglevel);
 
765
}
 
766
 
 
767
 
 
768
/**
 
769
 * Turns this program into a daemon.  In doing so, we fork() and kill the
 
770
 * parent process.  Note too that stdout, stdin, and stderr are closed in
 
771
 * daemon mode, and a file descriptor for a log file is opened.
 
772
 *      @param logp Path of the log file, NULL if no log file was specified
 
773
 *      @param logf Syslog facility, 0 if no facility was specified
 
774
 *      @param daemon False to turn off daemon mode (no fork, leave FDs open)
 
775
 *      @param logv Log verbosity, 2 is the default, 0 = no logging, 5 = everything
 
776
 *      @return 0 on success, > 0 on error
 
777
 */
 
778
int cgre_start_daemon(const char *logp, const int logf,
 
779
                        const unsigned char daemon, const int logv)
 
780
{
 
781
        /* PID returned from the fork() */
 
782
        pid_t pid;
 
783
 
 
784
        /* Fork and die. */
 
785
        if (daemon) {
 
786
                pid = fork();
 
787
                if (pid < 0) {
 
788
                        openlog("CGRE", LOG_CONS, LOG_DAEMON|LOG_WARNING);
 
789
                        syslog(LOG_DAEMON|LOG_WARNING, "Failed to fork,"
 
790
                                        " error: %s", strerror(errno));
 
791
                        closelog();
 
792
                        fprintf(stderr, "Failed to fork(), %s\n",
 
793
                                        strerror(errno));
 
794
                        return 1;
 
795
                } else if (pid > 0) {
 
796
                        exit(EXIT_SUCCESS);
 
797
                }
 
798
 
 
799
                /* Change the file mode mask. */
 
800
                umask(0);
 
801
        } else {
 
802
                cgroup_dbg("Not using daemon mode.\n");
 
803
                pid = getpid();
 
804
        }
 
805
 
 
806
        cgre_start_log(logp, logf, logv);
 
807
 
 
808
        if (!daemon) {
 
809
                /* We can skip the rest, since we're not becoming a daemon. */
 
810
                flog(LOG_INFO, "Proceeding with PID %d", getpid());
 
811
                return 0;
 
812
        } else {
 
813
                /* Get a new SID for the child. */
 
814
                if (setsid() < 0) {
 
815
                        flog(LOG_ERR, "Failed to get a new SID, error: %s",
 
816
                                        strerror(errno));
 
817
                        return 2;
 
818
                }
 
819
 
 
820
                /* Change to the root directory. */
 
821
                if (chdir("/") < 0) {
 
822
                        flog(LOG_ERR, "Failed to chdir to /, error: %s",
 
823
                                        strerror(errno));
 
824
                        return 3;
 
825
                }
 
826
 
 
827
                /* Close standard file descriptors. */
 
828
                close(STDIN_FILENO);
 
829
                if (logfile != stdout)
 
830
                        close(STDOUT_FILENO);
 
831
                close(STDERR_FILENO);
 
832
        }
 
833
 
 
834
        /* If we make it this far, we're a real daemon! Or we chose not to.  */
 
835
        flog(LOG_INFO, "Proceeding with PID %d", getpid());
 
836
        return 0;
 
837
}
 
838
 
 
839
/**
 
840
 * Catch the SIGUSR2 signal and reload the rules configuration.  This function
 
841
 * makes use of the logfile and flog() to print the new rules.
 
842
 *      @param signum The signal that we caught (always SIGUSR2)
 
843
 */
 
844
void cgre_flash_rules(int signum)
 
845
{
 
846
        /* Current time */
 
847
        time_t tm = time(0);
 
848
 
 
849
        flog(LOG_NOTICE, "Reloading rules configuration.");
 
850
        flog(LOG_DEBUG, "Current time: %s", ctime(&tm));
 
851
 
 
852
        /* Ask libcgroup to reload the rules table. */
 
853
        cgroup_reload_cached_rules();
 
854
 
 
855
        /* Print the results of the new table to our log file. */
 
856
        if (logfile && loglevel >= LOG_INFO) {
 
857
                cgroup_print_rules_config(logfile);
 
858
                fprintf(logfile, "\n");
 
859
        }
 
860
}
 
861
 
 
862
/**
 
863
 * Catch the SIGTERM and SIGINT signals so that we can exit gracefully.  Before
 
864
 * exiting, this function makes use of the logfile and flog().
 
865
 *      @param signum The signal that we caught (SIGTERM, SIGINT)
 
866
 */
 
867
void cgre_catch_term(int signum)
 
868
{
 
869
        /* Current time */
 
870
        time_t tm = time(0);
 
871
 
 
872
        flog(LOG_NOTICE, "Stopped CGroup Rules Engine Daemon at %s",
 
873
                        ctime(&tm));
 
874
 
 
875
        /* Close the log file, if we opened one */
 
876
        if (logfile && logfile != stdout)
 
877
                fclose(logfile);
 
878
 
 
879
        /* Close syslog */
 
880
        if (logfacility)
 
881
                closelog();
 
882
 
 
883
        exit(EXIT_SUCCESS);
 
884
}
 
885
 
 
886
/**
 
887
 * Parse the syslog facility as received on command line.
 
888
 *      @param arg Command line argument with the syslog facility
 
889
 *      @return the syslog facility (e.g. LOG_DAEMON) or 0 on error
 
890
 */
 
891
static int cgre_parse_syslog_facility(const char *arg)
 
892
{
 
893
    if (arg == NULL)
 
894
        return 0;
 
895
 
 
896
    if (strlen(arg) > 1)
 
897
        return 0;
 
898
 
 
899
        switch (arg[0]) {
 
900
        case '0':
 
901
                return LOG_LOCAL0;
 
902
        case '1':
 
903
                return LOG_LOCAL1;
 
904
        case '2':
 
905
                return LOG_LOCAL2;
 
906
        case '3':
 
907
                return LOG_LOCAL3;
 
908
        case '4':
 
909
                return LOG_LOCAL4;
 
910
        case '5':
 
911
                return LOG_LOCAL5;
 
912
        case '6':
 
913
                return LOG_LOCAL6;
 
914
        case '7':
 
915
                return LOG_LOCAL7;
 
916
        case 'D':
 
917
                return LOG_DAEMON;
 
918
        default:
 
919
                return 0;
 
920
        }
 
921
}
 
922
 
 
923
int main(int argc, char *argv[])
 
924
{
 
925
        /* Patch to the log file */
 
926
        const char *logp = NULL;
 
927
 
 
928
        /* Syslog facility */
 
929
        int facility = 0;
 
930
 
 
931
        /* Verbose level */
 
932
        int verbosity = 2;
 
933
 
 
934
        /* For catching signals */
 
935
        struct sigaction sa;
 
936
 
 
937
        /* Should we daemonize? */
 
938
        unsigned char daemon = 1;
 
939
 
 
940
        /* Return codes */
 
941
        int ret = 0;
 
942
 
 
943
        /* Command line arguments */
 
944
        const char *short_options = "hvqf:s::ndQ";
 
945
        struct option long_options[] = {
 
946
                {"help", no_argument, NULL, 'h'},
 
947
                {"verbose", no_argument, NULL, 'v'},
 
948
                {"quiet", no_argument, NULL, 'q'},
 
949
                {"logfile", required_argument, NULL, 'f'},
 
950
                {"syslog", optional_argument, NULL, 's'},
 
951
                {"nodaemon", no_argument, NULL, 'n'},
 
952
                {"debug", no_argument, NULL, 'd'},
 
953
                {"nolog", no_argument, NULL, 'Q'},
 
954
                {NULL, 0, NULL, 0}
 
955
        };
 
956
 
 
957
        /* Make sure the user is root. */
 
958
        if (getuid() != 0) {
 
959
                fprintf(stderr, "Error: Only root can start/stop the control"
 
960
                                " group rules engine daemon\n");
 
961
                ret = 1;
 
962
                goto finished;
 
963
        }
 
964
 
 
965
        while (1) {
 
966
                int c;
 
967
 
 
968
                c = getopt_long(argc, argv, short_options, long_options, NULL);
 
969
                if (c == -1)
 
970
                        break;
 
971
 
 
972
                switch (c) {
 
973
                case 'h':   /* --help */
 
974
                        usage(stdout, "Help:\n");
 
975
                        ret = 0;
 
976
                        goto finished;
 
977
 
 
978
                case 'v':   /* --verbose */
 
979
                        verbosity++;
 
980
                        break;
 
981
 
 
982
                case 'q':   /* --quiet */
 
983
                        verbosity--;
 
984
                        break;
 
985
 
 
986
                case 'Q':   /* --nolog */
 
987
                        verbosity = 0;
 
988
                        break;
 
989
 
 
990
                case 'f':   /* --logfile=<filename> */
 
991
                        logp = optarg;
 
992
                        break;
 
993
 
 
994
                case 's':   /* --syslog=[facility] */
 
995
                        if (optarg) {
 
996
                                facility = cgre_parse_syslog_facility(optarg);
 
997
                                if (facility == 0) {
 
998
                                        fprintf(stderr,
 
999
                                                "Unknown syslog facility: %s\n",
 
1000
                                                optarg);
 
1001
                                        ret = 2;
 
1002
                                        goto finished;
 
1003
                                }
 
1004
                        } else {
 
1005
                                facility = LOG_DAEMON;
 
1006
                        }
 
1007
                        break;
 
1008
 
 
1009
                case 'n':   /* --no-fork */
 
1010
                        daemon = 0;
 
1011
                        break;
 
1012
 
 
1013
                case 'd':   /* --debug */
 
1014
                        /* same as -vvn */
 
1015
                        daemon = 0;
 
1016
                        verbosity = 4;
 
1017
                        logp = "-";
 
1018
                        break;
 
1019
 
 
1020
                default:
 
1021
                        usage(stderr, "");
 
1022
                        ret = 2;
 
1023
                        goto finished;
 
1024
                }
 
1025
        }
 
1026
 
 
1027
        /* Initialize libcgroup. */
 
1028
        if ((ret = cgroup_init()) != 0) {
 
1029
                fprintf(stderr, "Error: libcgroup initialization failed, %d\n",
 
1030
                                ret);
 
1031
                goto finished;
 
1032
        }
 
1033
 
 
1034
        /* Ask libcgroup to load the configuration rules. */
 
1035
        if ((ret = cgroup_init_rules_cache()) != 0) {
 
1036
                fprintf(stderr, "Error: libcgroup failed to initialize rules"
 
1037
                                "cache, %d\n", ret);
 
1038
                goto finished;
 
1039
        }
 
1040
 
 
1041
        /* Now, start the daemon. */
 
1042
        ret = cgre_start_daemon(logp, facility, daemon, verbosity);
 
1043
        if (ret < 0) {
 
1044
                fprintf(stderr, "Error: Failed to launch the daemon, %d\n",
 
1045
                        ret);
 
1046
                goto finished;
 
1047
        }
 
1048
 
 
1049
        /*
 
1050
         * Set up the signal handler to reload the cached rules upon reception
 
1051
         * of a SIGUSR2 signal.
 
1052
         */
 
1053
        sa.sa_handler = &cgre_flash_rules;
 
1054
        sa.sa_flags = 0;
 
1055
        sa.sa_restorer = NULL;
 
1056
        sigemptyset(&sa.sa_mask);
 
1057
        if ((ret = sigaction(SIGUSR2, &sa, NULL))) {
 
1058
                flog(LOG_ERR, "Failed to set up signal handler for SIGUSR2."
 
1059
                                " Error: %s", strerror(errno));
 
1060
                goto finished;
 
1061
        }
 
1062
 
 
1063
        /*
 
1064
         * Set up the signal handler to catch SIGINT and SIGTERM so that we
 
1065
         * can exit gracefully.
 
1066
         */
 
1067
        sa.sa_handler = &cgre_catch_term;
 
1068
        ret = sigaction(SIGINT, &sa, NULL);
 
1069
        ret |= sigaction(SIGTERM, &sa, NULL);
 
1070
        if (ret) {
 
1071
                flog(LOG_ERR, "Failed to set up the signal handler.  Error:"
 
1072
                                " %s", strerror(errno));
 
1073
                goto finished;
 
1074
        }
 
1075
 
 
1076
        /* Print the configuration to the log file, or stdout. */
 
1077
        if (logfile && loglevel >= LOG_INFO)
 
1078
                cgroup_print_rules_config(logfile);
 
1079
 
 
1080
        flog(LOG_NOTICE, "Started the CGroup Rules Engine Daemon.");
 
1081
 
 
1082
        /* We loop endlesly in this function, unless we encounter an error. */
 
1083
        ret =  cgre_create_netlink_socket_process_msg();
 
1084
 
 
1085
finished:
 
1086
        if (logfile && logfile != stdout)
 
1087
                fclose(logfile);
 
1088
 
 
1089
        return ret;
 
1090
}