]>
Commit | Line | Data |
---|---|---|
d7448a72 PR |
1 | /* |
2 | * Copyright (C) 2011 Red Hat, Inc. All rights reserved. | |
3 | * | |
4 | * This copyrighted material is made available to anyone wishing to use, | |
5 | * modify, copy, or redistribute it subject to the terms and conditions | |
6 | * of the GNU Lesser General Public License v.2.1. | |
7 | * | |
8 | * You should have received a copy of the GNU Lesser General Public License | |
9 | * along with this program; if not, write to the Free Software Foundation, | |
10 | * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
11 | */ | |
12 | ||
13 | #include <dlfcn.h> | |
14 | #include <errno.h> | |
15 | #include <pthread.h> | |
16 | #include <sys/file.h> | |
17 | #include <sys/stat.h> | |
18 | #include <sys/wait.h> | |
19 | #include <sys/time.h> | |
20 | #include <sys/resource.h> | |
21 | #include <unistd.h> | |
22 | #include <signal.h> | |
23 | ||
24 | #include <syslog.h> | |
25 | ||
26 | /* Create a device monitoring thread. */ | |
27 | static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int stacksize) | |
28 | { | |
29 | pthread_attr_t attr; | |
30 | pthread_attr_init(&attr); | |
31 | /* | |
32 | * We use a smaller stack since it gets preallocated in its entirety | |
33 | */ | |
34 | pthread_attr_setstacksize(&attr, stacksize); | |
35 | return pthread_create(t, &attr, fun, arg); | |
36 | } | |
37 | ||
38 | static volatile sig_atomic_t _shutdown_requested = 0; | |
39 | ||
40 | static void _exit_handler(int sig __attribute__((unused))) | |
41 | { | |
42 | _shutdown_requested = 1; | |
43 | } | |
44 | ||
45 | #ifdef linux | |
46 | # define OOM_ADJ_FILE "/proc/self/oom_adj" | |
47 | ||
48 | /* From linux/oom.h */ | |
49 | # define OOM_DISABLE (-17) | |
50 | # define OOM_ADJUST_MIN (-16) | |
51 | ||
52 | /* | |
53 | * Protection against OOM killer if kernel supports it | |
54 | */ | |
55 | static int _set_oom_adj(int val) | |
56 | { | |
57 | FILE *fp; | |
58 | ||
59 | struct stat st; | |
60 | ||
61 | if (stat(OOM_ADJ_FILE, &st) == -1) { | |
62 | if (errno == ENOENT) | |
63 | perror(OOM_ADJ_FILE " not found"); | |
64 | else | |
65 | perror(OOM_ADJ_FILE ": stat failed"); | |
66 | return 1; | |
67 | } | |
68 | ||
69 | if (!(fp = fopen(OOM_ADJ_FILE, "w"))) { | |
70 | perror(OOM_ADJ_FILE ": fopen failed"); | |
71 | return 0; | |
72 | } | |
73 | ||
74 | fprintf(fp, "%i", val); | |
75 | if (dm_fclose(fp)) | |
76 | perror(OOM_ADJ_FILE ": fclose failed"); | |
77 | ||
78 | return 1; | |
79 | } | |
80 | #endif | |
81 | ||
82 | static void remove_lockfile(const char *file) | |
83 | { | |
84 | if (unlink(file)) | |
85 | perror(file ": unlink failed"); | |
86 | } | |
87 | ||
88 | static void _daemonise(void) | |
89 | { | |
90 | int child_status; | |
91 | int fd; | |
92 | pid_t pid; | |
93 | struct rlimit rlim; | |
94 | struct timeval tval; | |
95 | sigset_t my_sigset; | |
96 | ||
97 | sigemptyset(&my_sigset); | |
98 | if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) { | |
99 | fprintf(stderr, "Unable to restore signals.\n"); | |
100 | exit(EXIT_FAILURE); | |
101 | } | |
102 | signal(SIGTERM, &_exit_handler); | |
103 | ||
104 | switch (pid = fork()) { | |
105 | case -1: | |
106 | perror("fork failed:"); | |
107 | exit(EXIT_FAILURE); | |
108 | ||
109 | case 0: /* Child */ | |
110 | break; | |
111 | ||
112 | default: | |
113 | /* Wait for response from child */ | |
114 | while (!waitpid(pid, &child_status, WNOHANG) && !_exit_now) { | |
115 | tval.tv_sec = 0; | |
116 | tval.tv_usec = 250000; /* .25 sec */ | |
117 | select(0, NULL, NULL, NULL, &tval); | |
118 | } | |
119 | ||
120 | if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */ | |
121 | exit(0); | |
122 | ||
123 | /* Problem with child. Determine what it is by exit code */ | |
124 | switch (WEXITSTATUS(child_status)) { | |
125 | case EXIT_DESC_CLOSE_FAILURE: | |
126 | case EXIT_DESC_OPEN_FAILURE: | |
127 | case EXIT_FIFO_FAILURE: | |
128 | case EXIT_CHDIR_FAILURE: | |
129 | default: | |
130 | fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status)); | |
131 | break; | |
132 | } | |
133 | ||
134 | exit(WEXITSTATUS(child_status)); | |
135 | } | |
136 | ||
137 | if (chdir("/")) | |
138 | exit(1); | |
139 | ||
140 | if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) | |
141 | fd = 256; /* just have to guess */ | |
142 | else | |
143 | fd = rlim.rlim_cur; | |
144 | ||
145 | for (--fd; fd >= 0; fd--) | |
146 | close(fd); | |
147 | ||
148 | if ((open("/dev/null", O_RDONLY) < 0) || | |
149 | (open("/dev/null", O_WRONLY) < 0) || | |
150 | (open("/dev/null", O_WRONLY) < 0)) | |
151 | exit(1); | |
152 | ||
153 | setsid(); | |
154 | } | |
155 | ||
156 | void daemon_start(daemon_state s, handle_request r) | |
157 | { | |
158 | /* | |
159 | * Switch to C locale to avoid reading large locale-archive file used by | |
160 | * some glibc (on some distributions it takes over 100MB). Some daemons | |
161 | * need to use mlockall(). | |
162 | */ | |
163 | if (setenv("LANG", "C", 1)) | |
164 | perror("Cannot set LANG to C"); | |
165 | ||
166 | if (!s.foreground) | |
167 | _daemonise(); | |
168 | ||
169 | /* TODO logging interface should be somewhat more elaborate */ | |
170 | openlog(s.name, LOG_PID, LOG_DAEMON); | |
171 | ||
172 | (void) dm_prepare_selinux_context(s.pidfile, S_IFREG); | |
173 | ||
174 | /* | |
175 | * NB. Past this point, exit is not allowed. You have to return to this | |
176 | * function at all costs. More or less. | |
177 | */ | |
178 | if (dm_create_lockfile(s.pidfile) == 0) | |
179 | exit(1); | |
180 | ||
181 | (void) dm_prepare_selinux_context(NULL, 0); | |
182 | ||
183 | /* Set normal exit signals to request shutdown instead of dying. */ | |
184 | signal(SIGINT, &_exit_handler); | |
185 | signal(SIGHUP, &_exit_handler); | |
186 | signal(SIGQUIT, &_exit_handler); | |
187 | ||
188 | #ifdef linux | |
189 | if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) | |
190 | syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); | |
191 | #endif | |
192 | ||
193 | /* Signal parent, letting them know we are ready to go. */ | |
194 | if (!s.foreground) | |
195 | kill(getppid(), SIGTERM); | |
196 | ||
197 | while (!_shutdown_requested) { | |
198 | /* TODO: do work */ | |
199 | } | |
200 | ||
201 | syslog(LOG_NOTICE, "%s shutting down", s.name); | |
202 | closelog(); | |
203 | remove_lockfile(s.pidfile); | |
204 | } |