/[baselayout]/trunk/src/librc-daemon.c
Gentoo

Contents of /trunk/src/librc-daemon.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (show annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (7 years, 4 months ago) by uberlord
File MIME type: text/x-csrc
File size: 11866 byte(s)
    Rewrite the core parts in C. We now provide librc so other programs can
    query runlevels, services and state without using bash. We also provide
    libeinfo so other programs can easily use our informational functions.

    As such, we have dropped the requirement of using bash as the init script
    shell. We now use /bin/sh and have strived to make the scripts as portable
    as possible. Shells that work are bash and dash. busybox works provided
    you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
    should disable find too.
    zsh and ksh do not work at this time.

    Networking support is currently being re-vamped also as it was heavily bash
    array based. As such, a new config format is available like so
    config_eth0="1.2.3.4/24 5.6.7.8/16"
    or like so
    config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

    We will still support the old bash array format provided that /bin/sh IS
    a link it bash.

    ChangeLog for baselayout-1 can be found in our SVN repo.
1 /*
2 librc-daemon
3 Finds PID for given daemon criteria
4 Copyright 2007 Gentoo Foundation
5 Released under the GPLv2
6 */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10
11 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)
12 #include <sys/param.h>
13 #include <sys/user.h>
14 #include <sys/sysctl.h>
15 #include <kvm.h>
16 #include <limits.h>
17 #endif
18
19 #ifndef __linux__
20 #include <libgen.h>
21 #endif
22
23 #include <dirent.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "einfo.h"
32 #include "rc.h"
33 #include "rc-misc.h"
34 #include "strlist.h"
35
36 #if defined(__linux__)
37 static bool pid_is_cmd (pid_t pid, const char *cmd)
38 {
39 char buffer[32];
40 FILE *fp;
41 int c;
42
43 snprintf(buffer, sizeof (buffer), "/proc/%d/stat", pid);
44 if ((fp = fopen (buffer, "r")) == NULL)
45 return (false);
46
47 while ((c = getc (fp)) != EOF && c != '(')
48 ;
49
50 if (c != '(')
51 {
52 fclose(fp);
53 return (false);
54 }
55
56 while ((c = getc (fp)) != EOF && c == *cmd)
57 cmd++;
58
59 fclose (fp);
60
61 return ((c == ')' && *cmd == '\0') ? true : false);
62 }
63
64 static bool pid_is_exec (pid_t pid, const char *exec)
65 {
66 char cmdline[32];
67 char buffer[PATH_MAX];
68 char *p;
69 int fd = -1;
70 int r;
71
72 snprintf (cmdline, sizeof (cmdline), "/proc/%u/exe", pid);
73 memset (buffer, 0, sizeof (buffer));
74 if (readlink (cmdline, buffer, sizeof (buffer)) != -1)
75 {
76 if (strcmp (exec, buffer) == 0)
77 return (true);
78
79 /* We should cater for deleted binaries too */
80 if (strlen (buffer) > 10)
81 {
82 p = buffer + (strlen (buffer) - 10);
83 if (strcmp (p, " (deleted)") == 0)
84 {
85 *p = 0;
86 if (strcmp (buffer, exec) == 0)
87 return (true);
88 }
89 }
90 }
91
92 snprintf (cmdline, sizeof (cmdline), "/proc/%u/cmdline", pid);
93 if ((fd = open (cmdline, O_RDONLY)) < 0)
94 return (false);
95
96 r = read(fd, buffer, sizeof (buffer));
97 close (fd);
98
99 if (r == -1)
100 return 0;
101
102 buffer[r] = 0;
103 return (strcmp (exec, buffer) == 0 ? true : false);
104 }
105
106 pid_t *rc_find_pids (const char *exec, const char *cmd,
107 uid_t uid, pid_t pid)
108 {
109 DIR *procdir;
110 struct dirent *entry;
111 int npids = 0;
112 int foundany = false;
113 pid_t p;
114 pid_t *pids = NULL;
115 char buffer[PATH_MAX];
116 struct stat sb;
117 pid_t runscript_pid = 0;
118 char *pp;
119
120 if ((procdir = opendir ("/proc")) == NULL)
121 eerrorx ("opendir `/proc': %s", strerror (errno));
122
123 /*
124 We never match RC_RUNSCRIPT_PID if present so we avoid the below
125 scenario
126
127 /etc/init.d/ntpd stop does
128 start-stop-daemon --stop --name ntpd
129 catching /etc/init.d/ntpd stop
130
131 nasty
132 */
133
134 if ((pp = getenv ("RC_RUNSCRIPT_PID")))
135 {
136 if (sscanf (pp, "%d", &runscript_pid) != 1)
137 runscript_pid = 0;
138 }
139
140 while ((entry = readdir (procdir)) != NULL)
141 {
142 if (sscanf (entry->d_name, "%d", &p) != 1)
143 continue;
144 foundany = true;
145
146 if (runscript_pid != 0 && runscript_pid == p)
147 continue;
148
149 if (pid != 0 && pid != p)
150 continue;
151
152 if (uid)
153 {
154 snprintf (buffer, sizeof (buffer), "/proc/%d", pid);
155 if (stat (buffer, &sb) != 0 || sb.st_uid != uid)
156 continue;
157 }
158
159 if (cmd && ! pid_is_cmd (p, cmd))
160 continue;
161
162 if (exec && ! cmd && ! pid_is_exec (p, exec))
163 continue;
164
165 pids = realloc (pids, sizeof (pid_t) * (npids + 2));
166 if (! pids)
167 eerrorx ("memory exhausted");
168
169 pids[npids] = p;
170 pids[npids + 1] = 0;
171 npids++;
172 }
173 closedir (procdir);
174
175 if (! foundany)
176 eerrorx ("nothing in /proc");
177
178 return (pids);
179 }
180
181 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
182
183 # if defined(__FreeBSD__)
184 # define _KINFO_PROC kinfo_proc
185 # define _KVM_GETPROCS kvm_getprocs
186 # define _KVM_GETARGV kvm_getargv
187 # define _GET_KINFO_UID(kp) (kp.ki_ruid)
188 # define _GET_KINFO_COMM(kp) (kp.ki_comm)
189 # define _GET_KINFO_PID(kp) (kp.ki_pid)
190 # else
191 # define _KINFO_PROC kinfo_proc2
192 # define _KVM_GETPROCS kvm_getprocs2
193 # define _KVM_GETARGV kvm_getargv2
194 # define _GET_KINFO_UID(kp) (kp.p_ruid)
195 # define _GET_KINFO_COMM(kp) (kp.p_comm)
196 # define _GET_KINFO_PID(kp) (kp.p_pid)
197 # endif
198
199 pid_t *rc_find_pids (const char *exec, const char *cmd,
200 uid_t uid, pid_t pid)
201 {
202 static kvm_t *kd = NULL;
203 char errbuf[_POSIX2_LINE_MAX];
204 struct _KINFO_PROC *kp;
205 int i;
206 int processes = 0;
207 int argc = 0;
208 char **argv;
209 pid_t *pids = NULL;
210 int npids = 0;
211
212 if ((kd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL)
213 eerrorx ("kvm_open: %s", errbuf);
214
215 kp = _KVM_GETPROCS (kd, KERN_PROC_PROC, 0, &processes);
216 for (i = 0; i < processes; i++)
217 {
218 pid_t p = _GET_KINFO_PID (kp[i]);
219 if (pid != 0 && pid != p)
220 continue;
221
222 if (uid != 0 && uid != _GET_KINFO_UID (kp[i]))
223 continue;
224
225 if (cmd)
226 {
227 if (! _GET_KINFO_COMM (kp[i]) ||
228 strcmp (cmd, _GET_KINFO_COMM (kp[i])) != 0)
229 continue;
230 }
231
232 if (exec && ! cmd)
233 {
234 if ((argv = _KVM_GETARGV (kd, &kp[i], argc)) == NULL || ! *argv)
235 continue;
236
237 if (strcmp (*argv, exec) != 0)
238 continue;
239 }
240
241 pids = realloc (pids, sizeof (pid_t) * (npids + 2));
242 if (! pids)
243 eerrorx ("memory exhausted");
244
245 pids[npids] = p;
246 pids[npids + 1] = 0;
247 npids++;
248 }
249 kvm_close(kd);
250
251 return (pids);
252 }
253
254 #else
255 # error "Platform not supported!"
256 #endif
257
258 static bool _match_daemon (const char *path, const char *file,
259 const char *mexec, const char *mname,
260 const char *mpidfile)
261 {
262 char buffer[RC_LINEBUFFER];
263 char *ffile = rc_strcatpaths (path, file, NULL);
264 FILE *fp;
265 int lc = 0;
266 int m = 0;
267
268 if (! rc_exists (ffile))
269 {
270 free (ffile);
271 return (false);
272 }
273
274 if ((fp = fopen (ffile, "r")) == NULL)
275 {
276 eerror ("fopen `%s': %s", ffile, strerror (errno));
277 free (ffile);
278 return (false);
279 }
280
281 if (! mname)
282 m += 10;
283 if (! mpidfile)
284 m += 100;
285
286 memset (buffer, 0, sizeof (buffer));
287 while ((fgets (buffer, RC_LINEBUFFER, fp)))
288 {
289 int lb = strlen (buffer) - 1;
290 if (buffer[lb] == '\n')
291 buffer[lb] = 0;
292
293 if (strcmp (buffer, mexec) == 0)
294 m += 1;
295 else if (mname && strcmp (buffer, mname) == 0)
296 m += 10;
297 else if (mpidfile && strcmp (buffer, mpidfile) == 0)
298 m += 100;
299
300 if (m == 111)
301 break;
302
303 lc++;
304 if (lc > 5)
305 break;
306 }
307 fclose (fp);
308 free (ffile);
309
310 return (m == 111 ? true : false);
311 }
312
313 void rc_set_service_daemon (const char *service, const char *exec,
314 const char *name, const char *pidfile,
315 bool started)
316 {
317 char *dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
318 char **files = NULL;
319 char *file;
320 char *ffile = NULL;
321 int i;
322 char *mexec;
323 char *mname;
324 char *mpidfile;
325 int nfiles = 0;
326
327 if (! exec && ! name && ! pidfile)
328 return;
329
330 if (exec)
331 {
332 i = strlen (exec) + 6;
333 mexec = rc_xmalloc (sizeof (char *) * i);
334 snprintf (mexec, i, "exec=%s", exec);
335 }
336 else
337 mexec = strdup ("exec=");
338
339 if (name)
340 {
341 i = strlen (name) + 6;
342 mname = rc_xmalloc (sizeof (char *) * i);
343 snprintf (mname, i, "name=%s", name);
344 }
345 else
346 mname = strdup ("name=");
347
348 if (pidfile)
349 {
350 i = strlen (pidfile) + 9;
351 mpidfile = rc_xmalloc (sizeof (char *) * i);
352 snprintf (mpidfile, i, "pidfile=%s", pidfile);
353 }
354 else
355 mpidfile = strdup ("pidfile=");
356
357 /* Regardless, erase any existing daemon info */
358 if (rc_is_dir (dirpath))
359 {
360 char *oldfile = NULL;
361 files = rc_ls_dir (NULL, dirpath, 0);
362 STRLIST_FOREACH (files, file, i)
363 {
364 ffile = rc_strcatpaths (dirpath, file, NULL);
365 nfiles++;
366
367 if (! oldfile)
368 {
369 if (_match_daemon (dirpath, file, mexec, mname, mpidfile))
370 {
371 unlink (ffile);
372 oldfile = ffile;
373 nfiles--;
374 }
375 }
376 else
377 {
378 rename (ffile, oldfile);
379 free (oldfile);
380 oldfile = ffile;
381 }
382 }
383 if (ffile)
384 free (ffile);
385 free (files);
386 }
387
388 /* Now store our daemon info */
389 if (started)
390 {
391 char buffer[10];
392 FILE *fp;
393
394 if (! rc_is_dir (dirpath))
395 if (mkdir (dirpath, 0755) != 0)
396 eerror ("mkdir `%s': %s", dirpath, strerror (errno));
397
398 snprintf (buffer, sizeof (buffer), "%03d", nfiles + 1);
399 file = rc_strcatpaths (dirpath, buffer, NULL);
400 if ((fp = fopen (file, "w")) == NULL)
401 eerror ("fopen `%s': %s", file, strerror (errno));
402 else
403 {
404 fprintf (fp, "%s\n%s\n%s\n", mexec, mname, mpidfile);
405 fclose (fp);
406 }
407 free (file);
408 }
409
410 free (mexec);
411 free (mname);
412 free (mpidfile);
413 free (dirpath);
414 }
415
416 bool rc_service_started_daemon (const char *service, const char *exec,
417 int indx)
418 {
419 char *dirpath;
420 char *file;
421 int i;
422 char *mexec;
423 bool retval = false;
424
425 if (! service || ! exec)
426 return (false);
427
428 dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
429 if (! rc_is_dir (dirpath))
430 {
431 free (dirpath);
432 return (false);
433 }
434
435 i = strlen (exec) + 6;
436 mexec = rc_xmalloc (sizeof (char *) * i);
437 snprintf (mexec, i, "exec=%s", exec);
438
439 if (indx > 0)
440 {
441 file = rc_xmalloc (sizeof (char *) * 10);
442 snprintf (file, sizeof (file), "%03d", indx);
443 retval = _match_daemon (dirpath, file, mexec, NULL, NULL);
444 free (file);
445 }
446 else
447 {
448 char **files = rc_ls_dir (NULL, dirpath, 0);
449 STRLIST_FOREACH (files, file, i)
450 {
451 retval = _match_daemon (dirpath, file, mexec, NULL, NULL);
452 if (retval)
453 break;
454 }
455 free (files);
456 }
457
458 free (mexec);
459 return (retval);
460 }
461
462 bool rc_service_daemons_crashed (const char *service)
463 {
464 char *dirpath;
465 char **files;
466 char *file;
467 char *path;
468 int i;
469 FILE *fp;
470 char buffer[RC_LINEBUFFER];
471 char *exec = NULL;
472 char *name = NULL;
473 char *pidfile = NULL;
474 pid_t pid = 0;
475 pid_t *pids = NULL;
476 char *p;
477 char *token;
478 bool retval = false;
479
480 if (! service)
481 return (false);
482
483 dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
484 if (! rc_is_dir (dirpath))
485 {
486 free (dirpath);
487 return (false);
488 }
489
490 memset (buffer, 0, sizeof (buffer));
491 files = rc_ls_dir (NULL, dirpath, 0);
492 STRLIST_FOREACH (files, file, i)
493 {
494 path = rc_strcatpaths (dirpath, file, NULL);
495 fp = fopen (path, "r");
496 free (path);
497 if (! fp)
498 {
499 eerror ("fopen `%s': %s", file, strerror (errno));
500 continue;
501 }
502
503 while ((fgets (buffer, RC_LINEBUFFER, fp)))
504 {
505 int lb = strlen (buffer) - 1;
506 if (buffer[lb] == '\n')
507 buffer[lb] = 0;
508
509 p = buffer;
510 if ((token = strsep (&p, "=")) == NULL || ! p)
511 continue;
512
513 if (strlen (p) == 0)
514 continue;
515
516 if (strcmp (token, "exec") == 0)
517 {
518 if (exec)
519 free (exec);
520 exec = strdup (p);
521 }
522 else if (strcmp (token, "name") == 0)
523 {
524 if (name)
525 free (name);
526 name = strdup (p);
527 }
528 else if (strcmp (token, "pidfile") == 0)
529 {
530 if (pidfile)
531 free (pidfile);
532 pidfile = strdup (p);
533 }
534 }
535 fclose (fp);
536
537 pid = 0;
538 if (pidfile)
539 {
540 if (! rc_exists (pidfile))
541 {
542 retval = true;
543 break;
544 }
545
546 if ((fp = fopen (pidfile, "r")) == NULL)
547 {
548 eerror ("fopen `%s': %s", pidfile, strerror (errno));
549 retval = true;
550 break;
551 }
552
553 if (fscanf (fp, "%d", &pid) != 1)
554 {
555 eerror ("no pid found in `%s'", pidfile);
556 fclose (fp);
557 retval = true;
558 break;
559 }
560
561 fclose (fp);
562 free (pidfile);
563 pidfile = NULL;
564 }
565
566 if ((pids = rc_find_pids (exec, name, 0, pid)) == NULL)
567 {
568 retval = true;
569 break;
570 }
571 free (pids);
572
573 if (exec)
574 {
575 free (exec);
576 exec = NULL;
577 }
578 if (name)
579 {
580 free (name);
581 name = NULL;
582 }
583 }
584
585 if (exec)
586 {
587 free (exec);
588 exec = NULL;
589 }
590 if (name)
591 {
592 free (name);
593 name = NULL;
594 }
595
596 free (dirpath);
597 rc_strlist_free (files);
598
599 return (retval);
600 }

  ViewVC Help
Powered by ViewVC 1.1.20