]>
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 | ||
110 | memset(&sockaddr, 0, sizeof(sockaddr)); | |
111 | memcpy(sockaddr.sun_path, s.socket_path, strlen(s.socket_path)); | |
112 | sockaddr.sun_family = AF_UNIX; | |
113 | ||
114 | if (bind(fd, (struct sockaddr *) &sockaddr, sizeof(sockaddr))) { | |
dc85d3fb | 115 | perror("can't bind local socket."); |
73ffd6e7 PR |
116 | goto error; |
117 | } | |
118 | if (listen(fd, 1) != 0) { | |
dc85d3fb | 119 | perror("listen local"); |
73ffd6e7 PR |
120 | goto error; |
121 | } | |
122 | ||
123 | out: | |
124 | umask(old_mask); | |
125 | (void) dm_prepare_selinux_context(NULL, 0); | |
126 | return fd; | |
127 | ||
128 | error: | |
129 | if (fd >= 0) { | |
130 | close(fd); | |
131 | fd = -1; | |
132 | } | |
133 | goto out; | |
134 | } | |
135 | ||
d7448a72 PR |
136 | static void remove_lockfile(const char *file) |
137 | { | |
138 | if (unlink(file)) | |
dc85d3fb | 139 | perror("unlink failed"); |
d7448a72 PR |
140 | } |
141 | ||
142 | static void _daemonise(void) | |
143 | { | |
144 | int child_status; | |
145 | int fd; | |
146 | pid_t pid; | |
147 | struct rlimit rlim; | |
148 | struct timeval tval; | |
149 | sigset_t my_sigset; | |
150 | ||
151 | sigemptyset(&my_sigset); | |
152 | if (sigprocmask(SIG_SETMASK, &my_sigset, NULL) < 0) { | |
153 | fprintf(stderr, "Unable to restore signals.\n"); | |
154 | exit(EXIT_FAILURE); | |
155 | } | |
156 | signal(SIGTERM, &_exit_handler); | |
157 | ||
158 | switch (pid = fork()) { | |
159 | case -1: | |
160 | perror("fork failed:"); | |
161 | exit(EXIT_FAILURE); | |
162 | ||
163 | case 0: /* Child */ | |
164 | break; | |
165 | ||
166 | default: | |
167 | /* Wait for response from child */ | |
dc85d3fb | 168 | while (!waitpid(pid, &child_status, WNOHANG) && !_shutdown_requested) { |
d7448a72 PR |
169 | tval.tv_sec = 0; |
170 | tval.tv_usec = 250000; /* .25 sec */ | |
171 | select(0, NULL, NULL, NULL, &tval); | |
172 | } | |
173 | ||
174 | if (_shutdown_requested) /* Child has signaled it is ok - we can exit now */ | |
175 | exit(0); | |
176 | ||
177 | /* Problem with child. Determine what it is by exit code */ | |
dc85d3fb | 178 | fprintf(stderr, "Child exited with code %d\n", WEXITSTATUS(child_status)); |
d7448a72 PR |
179 | exit(WEXITSTATUS(child_status)); |
180 | } | |
181 | ||
182 | if (chdir("/")) | |
183 | exit(1); | |
184 | ||
185 | if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) | |
186 | fd = 256; /* just have to guess */ | |
187 | else | |
188 | fd = rlim.rlim_cur; | |
189 | ||
190 | for (--fd; fd >= 0; fd--) | |
191 | close(fd); | |
192 | ||
193 | if ((open("/dev/null", O_RDONLY) < 0) || | |
194 | (open("/dev/null", O_WRONLY) < 0) || | |
195 | (open("/dev/null", O_WRONLY) < 0)) | |
196 | exit(1); | |
197 | ||
198 | setsid(); | |
199 | } | |
200 | ||
dc85d3fb | 201 | void daemon_start(daemon_state s) |
d7448a72 | 202 | { |
73ffd6e7 | 203 | int failed = 0; |
d7448a72 PR |
204 | /* |
205 | * Switch to C locale to avoid reading large locale-archive file used by | |
206 | * some glibc (on some distributions it takes over 100MB). Some daemons | |
207 | * need to use mlockall(). | |
208 | */ | |
209 | if (setenv("LANG", "C", 1)) | |
210 | perror("Cannot set LANG to C"); | |
211 | ||
212 | if (!s.foreground) | |
213 | _daemonise(); | |
214 | ||
215 | /* TODO logging interface should be somewhat more elaborate */ | |
216 | openlog(s.name, LOG_PID, LOG_DAEMON); | |
217 | ||
218 | (void) dm_prepare_selinux_context(s.pidfile, S_IFREG); | |
219 | ||
220 | /* | |
73ffd6e7 PR |
221 | * NB. Take care to not keep stale locks around. Best not exit(...) |
222 | * after this point. | |
d7448a72 PR |
223 | */ |
224 | if (dm_create_lockfile(s.pidfile) == 0) | |
225 | exit(1); | |
226 | ||
227 | (void) dm_prepare_selinux_context(NULL, 0); | |
228 | ||
229 | /* Set normal exit signals to request shutdown instead of dying. */ | |
230 | signal(SIGINT, &_exit_handler); | |
231 | signal(SIGHUP, &_exit_handler); | |
232 | signal(SIGQUIT, &_exit_handler); | |
233 | ||
234 | #ifdef linux | |
235 | if (s.avoid_oom && !_set_oom_adj(OOM_DISABLE) && !_set_oom_adj(OOM_ADJUST_MIN)) | |
236 | syslog(LOG_ERR, "Failed to set oom_adj to protect against OOM killer"); | |
237 | #endif | |
238 | ||
73ffd6e7 PR |
239 | if (s.socket_path) { |
240 | s.socket_fd = _open_socket(s); | |
241 | if (s.socket_fd < 0) | |
242 | failed = 1; | |
243 | } | |
244 | ||
d7448a72 PR |
245 | /* Signal parent, letting them know we are ready to go. */ |
246 | if (!s.foreground) | |
247 | kill(getppid(), SIGTERM); | |
248 | ||
73ffd6e7 | 249 | while (!_shutdown_requested && !failed) { |
d7448a72 PR |
250 | /* TODO: do work */ |
251 | } | |
252 | ||
253 | syslog(LOG_NOTICE, "%s shutting down", s.name); | |
254 | closelog(); | |
255 | remove_lockfile(s.pidfile); | |
73ffd6e7 PR |
256 | if (failed) |
257 | exit(1); | |
d7448a72 | 258 | } |