]>
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> | |
dc85d3fb PR |
21 | #include <netinet/in.h> |
22 | #include <sys/un.h> | |
d7448a72 PR |
23 | #include <unistd.h> |
24 | #include <signal.h> | |
25 | ||
26 | #include <syslog.h> | |
dc85d3fb PR |
27 | #include "daemon-server.h" |
28 | #include "libdevmapper.h" | |
d7448a72 | 29 | |
dc85d3fb | 30 | #if 0 |
d7448a72 PR |
31 | /* Create a device monitoring thread. */ |
32 | static int _pthread_create(pthread_t *t, void *(*fun)(void *), void *arg, int stacksize) | |
33 | { | |
34 | pthread_attr_t attr; | |
35 | pthread_attr_init(&attr); | |
36 | /* | |
37 | * We use a smaller stack since it gets preallocated in its entirety | |
38 | */ | |
39 | pthread_attr_setstacksize(&attr, stacksize); | |
40 | return pthread_create(t, &attr, fun, arg); | |
41 | } | |
dc85d3fb | 42 | #endif |
d7448a72 PR |
43 | |
44 | static volatile sig_atomic_t _shutdown_requested = 0; | |
45 | ||
46 | static void _exit_handler(int sig __attribute__((unused))) | |
47 | { | |
48 | _shutdown_requested = 1; | |
49 | } | |
50 | ||
51 | #ifdef linux | |
52 | # define OOM_ADJ_FILE "/proc/self/oom_adj" | |
dc85d3fb | 53 | # include <stdio.h> |
d7448a72 PR |
54 | |
55 | /* From linux/oom.h */ | |
56 | # define OOM_DISABLE (-17) | |
57 | # define OOM_ADJUST_MIN (-16) | |
58 | ||
59 | /* | |
60 | * Protection against OOM killer if kernel supports it | |
61 | */ | |
62 | static int _set_oom_adj(int val) | |
63 | { | |
64 | FILE *fp; | |
65 | ||
66 | struct stat st; | |
67 | ||
68 | if (stat(OOM_ADJ_FILE, &st) == -1) { | |
69 | if (errno == ENOENT) | |
70 | perror(OOM_ADJ_FILE " not found"); | |
71 | else | |
72 | perror(OOM_ADJ_FILE ": stat failed"); | |
73 | return 1; | |
74 | } | |
75 | ||
76 | if (!(fp = fopen(OOM_ADJ_FILE, "w"))) { | |
77 | perror(OOM_ADJ_FILE ": fopen failed"); | |
78 | return 0; | |
79 | } | |
80 | ||
81 | fprintf(fp, "%i", val); | |
dc85d3fb | 82 | if (fclose(fp)) |
d7448a72 PR |
83 | perror(OOM_ADJ_FILE ": fclose failed"); |
84 | ||
85 | return 1; | |
86 | } | |
87 | #endif | |
88 | ||
73ffd6e7 PR |
89 | static int _open_socket(daemon_state s) |
90 | { | |
91 | int fd = -1; | |
92 | struct sockaddr_un sockaddr; | |
93 | mode_t old_mask; | |
94 | ||
95 | (void) dm_prepare_selinux_context(s.socket_path, S_IFSOCK); | |
96 | old_mask = umask(0077); | |
97 | ||
98 | /* Open local socket */ | |
99 | fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
100 | if (fd < 0) { | |
dc85d3fb | 101 | perror("Can't create local socket."); |
73ffd6e7 PR |
102 | goto error; |
103 | } | |
104 | ||
105 | /* Set Close-on-exec & non-blocking */ | |
106 | if (fcntl(fd, F_SETFD, 1)) | |
dc85d3fb | 107 | fprintf(stderr, "setting CLOEXEC on socket fd %d failed: %s\n", fd, strerror(errno)); |
73ffd6e7 PR |
108 | fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); |
109 | ||
55e30071 | 110 | fprintf(stderr, "[D] creating %s\n", s.socket_path); |
73ffd6e7 | 111 | memset(&sockaddr, 0, sizeof(sockaddr)); |
92658f56 | 112 | strcpy(sockaddr.sun_path, s.socket_path); |
73ffd6e7 PR |
113 | sockaddr.sun_family = AF_UNIX; |
114 | ||
115 | if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) { | |
dc85d3fb | 116 | perror("can't bind local socket."); |
73ffd6e7 PR |
117 | goto error; |
118 | } | |
119 | if (listen(fd, 1) != 0) { | |
dc85d3fb | 120 | perror("listen local"); |
73ffd6e7 PR |
121 | goto error; |
122 | } | |
123 | ||
124 | out: | |
125 | umask(old_mask); | |
126 | (void) dm_prepare_selinux_context(NULL, 0); | |
127 | return fd; | |
128 | ||
129 | error: | |
130 | if (fd >= 0) { | |
131 | close(fd); | |
92658f56 | 132 | unlink(s.socket_path); |
73ffd6e7 PR |
133 | fd = -1; |
134 | } | |
135 | goto out; | |
136 | } | |
137 | ||
d7448a72 PR |
138 | static void remove_lockfile(const char *file) |
139 | { | |
140 | if (unlink(file)) | |
dc85d3fb | 141 | perror("unlink failed"); |
d7448a72 PR |
142 | } |
143 | ||
144 | static void _daemonise(void) | |
145 | { | |
146 | int child_status; | |
147 | int fd; | |
148 | pid_t pid; | |
149 | struct rlimit rlim; | |
150 | struct timeval tval; | |
151 | sigset_t my_sigset; | |
152 | ||
153 | sigemptyset(&my_sigset); | |
154 | if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) { | |
155 | fprintf(stderr, "Unable to restore signals.\n"); | |
156 | exit(EXIT_FAILURE); | |
157 | } | |
158 | signal(SIGTERM, &_exit_handler); | |
159 | ||
160 | switch (pid = fork()) { | |
161 | case -1: | |
162 | perror("fork failed:"); | |
163 | exit(EXIT_FAILURE); | |
164 | ||
165 | case 0: /* Child */ | |
166 | break; | |
167 | ||
168 | default: | |
169 | /* Wait for response from child */ | |
dc85d3fb | 170 | while (!waitpid(pid, &child_status, WNOHANG) && !_shutdown_requested) { |
d7448a72 PR |
171 | tval.tv_sec = 0; |
172 | tval.tv_usec = 250000; /* .25 sec */ | |
173 | select(0, NULL, NULL, NULL, &tval); | |
174 | } | |
175 | ||
176 | if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */ | |
177 | exit(0); | |
178 | ||
179 | /* Problem with child. Determine what it is by exit code */ | |
dc85d3fb | 180 | fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status)); |
d7448a72 PR |
181 | exit(WEXITSTATUS(child_status)); |
182 | } | |
183 | ||
184 | if (chdir("/")) | |
185 | exit(1); | |
186 | ||
187 | if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) | |
188 | fd = 256; /* just have to guess */ | |
189 | else | |
190 | fd = rlim.rlim_cur; | |
191 | ||
192 | for (--fd; fd >= 0; fd--) | |
193 | close(fd); | |
194 | ||
195 | if ((open("/dev/null", O_RDONLY) < 0) || | |
196 | (open("/dev/null", O_WRONLY) < 0) || | |
197 | (open("/dev/null", O_WRONLY) < 0)) | |
198 | exit(1); | |
199 | ||
200 | setsid(); | |
201 | } | |
202 | ||
92658f56 PR |
203 | struct thread_baton { |
204 | daemon_state s; | |
205 | client_handle client; | |
206 | }; | |
207 | ||
208 | void *client_thread(void *baton) | |
209 | { | |
210 | struct thread_baton *b = baton; | |
211 | request req; | |
212 | while (1) { | |
213 | if (!read_buffer(b->client.socket_fd, &req.buffer)) | |
214 | goto fail; | |
215 | ||
55e30071 | 216 | req.cft = create_config_tree_from_string(req.buffer); |
92658f56 | 217 | response res = b->s.handler(b->s, b->client, req); |
55e30071 PR |
218 | destroy_config_tree(req.cft); |
219 | dm_free(req.buffer); | |
92658f56 PR |
220 | |
221 | if (!res.buffer) { | |
222 | /* TODO fill in the buffer from res.cft */ | |
223 | } | |
224 | ||
225 | write_buffer(b->client.socket_fd, res.buffer, strlen(res.buffer)); | |
226 | ||
227 | free(res.buffer); | |
92658f56 PR |
228 | } |
229 | fail: | |
230 | /* TODO what should we really do here? */ | |
231 | return NULL; | |
232 | } | |
233 | ||
234 | int handle_connect(daemon_state s) | |
235 | { | |
236 | struct sockaddr_un sockaddr; | |
237 | client_handle client; | |
238 | socklen_t sl = sizeof(sockaddr); | |
239 | int client_fd = accept(s.socket_fd, (struct sockaddr *) &sockaddr, &sl); | |
240 | if (client_fd < 0) | |
241 | return 0; | |
242 | ||
243 | struct thread_baton *baton = malloc(sizeof(struct thread_baton)); | |
244 | if (!baton) | |
245 | return 0; | |
246 | ||
247 | client.socket_fd = client_fd; | |
248 | client.read_buf = 0; | |
249 | client.private = 0; | |
250 | baton->s = s; | |
251 | baton->client = client; | |
252 | ||
253 | if (pthread_create(&baton->client.thread_id, NULL, client_thread, baton)) | |
254 | return 0; | |
255 | return 1; | |
256 | } | |
257 | ||
dc85d3fb | 258 | void daemon_start(daemon_state s) |
d7448a72 | 259 | { |
73ffd6e7 | 260 | int failed = 0; |
d7448a72 PR |
261 | /* |
262 | * Switch to C locale to avoid reading large locale-archive file used by | |
263 | * some glibc (on some distributions it takes over 100MB). Some daemons | |
264 | * need to use mlockall(). | |
265 | */ | |
266 | if (setenv("LANG", "C", 1)) | |
267 | perror("Cannot set LANG to C"); | |
268 | ||
269 | if (!s.foreground) | |
270 | _daemonise(); | |
271 | ||
272 | /* TODO logging interface should be somewhat more elaborate */ | |
273 | openlog(s.name, LOG_PID, LOG_DAEMON); | |
274 | ||
275 | (void) dm_prepare_selinux_context(s.pidfile, S_IFREG); | |
276 | ||
277 | /* | |
73ffd6e7 PR |
278 | * NB. Take care to not keep stale locks around. Best not exit(...) |
279 | * after this point. | |
d7448a72 PR |
280 | */ |
281 | if (dm_create_lockfile(s.pidfile) == 0) | |
282 | exit(1); | |
283 | ||
284 | (void) dm_prepare_selinux_context(NULL, 0); | |
285 | ||
286 | /* Set normal exit signals to request shutdown instead of dying. */ | |
287 | signal(SIGINT, &_exit_handler); | |
288 | signal(SIGHUP, &_exit_handler); | |
289 | signal(SIGQUIT, &_exit_handler); | |
92658f56 PR |
290 | signal(SIGTERM, &_exit_handler); |
291 | signal(SIGPIPE, SIG_IGN); | |
d7448a72 PR |
292 | |
293 | #ifdef linux | |
294 | if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) | |
295 | syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); | |
296 | #endif | |
297 | ||
73ffd6e7 PR |
298 | if (s.socket_path) { |
299 | s.socket_fd = _open_socket(s); | |
300 | if (s.socket_fd < 0) | |
301 | failed = 1; | |
302 | } | |
303 | ||
d7448a72 PR |
304 | /* Signal parent, letting them know we are ready to go. */ |
305 | if (!s.foreground) | |
306 | kill(getppid(), SIGTERM); | |
307 | ||
73ffd6e7 | 308 | while (!_shutdown_requested && !failed) { |
92658f56 PR |
309 | int status; |
310 | fd_set in; | |
311 | FD_ZERO(&in); | |
312 | FD_SET(s.socket_fd, &in); | |
313 | if (select(FD_SETSIZE, &in, NULL, NULL, NULL) < 0 && errno != EINTR) | |
314 | perror("select error"); | |
315 | if (FD_ISSET(s.socket_fd, &in)) | |
316 | if (!handle_connect(s)) | |
317 | syslog(LOG_ERR, "Failed to handle a client connection."); | |
d7448a72 PR |
318 | } |
319 | ||
92658f56 PR |
320 | if (s.socket_fd >= 0) |
321 | unlink(s.socket_path); | |
322 | ||
d7448a72 PR |
323 | syslog(LOG_NOTICE, "%s shutting down", s.name); |
324 | closelog(); | |
325 | remove_lockfile(s.pidfile); | |
73ffd6e7 PR |
326 | if (failed) |
327 | exit(1); | |
d7448a72 | 328 | } |