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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (show annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (12 years, 1 month ago) by uberlord
File MIME type: text/x-csrc
File size: 15602 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 rc-misc.c
3 rc misc functions
4 Copyright 2007 Gentoo Foundation
5 */
6
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/utsname.h>
10
11 #include <dirent.h>
12 #include <errno.h>
13 #include <limits.h>
14 #include <regex.h>
15 #include <stdarg.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #include "einfo.h"
22 #include "rc-misc.h"
23 #include "rc.h"
24 #include "strlist.h"
25
26 #define ERRX eerrorx("out of memory");
27
28 #define PROFILE_ENV "/etc/profile.env"
29 #define SYS_WHITELIST RC_LIBDIR "conf.d/env_whitelist"
30 #define USR_WHITELIST "/etc/conf.d/env_whitelist"
31 #define RC_CONFIG "/etc/conf.d/rc"
32
33 #define PATH_PREFIX RC_LIBDIR "bin:/bin:/sbin:/usr/bin:/usr/sbin"
34
35 #ifndef S_IXUGO
36 # define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
37 #endif
38
39 void *rc_xcalloc (size_t n, size_t size)
40 {
41 void *value = calloc (n, size);
42
43 if (value)
44 return value;
45
46 ERRX
47 }
48
49 void *rc_xmalloc (size_t size)
50 {
51 void *value = malloc (size);
52
53 if (value)
54 return (value);
55
56 ERRX
57 }
58
59 void *rc_xrealloc (void *ptr, size_t size)
60 {
61 void *value = realloc (ptr, size);
62
63 if (value)
64 return (value);
65
66 ERRX
67 }
68
69
70 char *rc_xstrdup (const char *str)
71 {
72 char *value;
73
74 if (! str)
75 return (NULL);
76
77 value = strdup (str);
78
79 if (value)
80 return (value);
81
82 ERRX
83 }
84
85 bool rc_is_env (const char *var, const char *val)
86 {
87 char *v;
88
89 if (! var)
90 return (false);
91
92 v = getenv (var);
93 if (! v)
94 return (val == NULL ? true : false);
95
96 return (strcasecmp (v, val) == 0 ? true : false);
97 }
98
99 char *rc_strcatpaths (const char *path1, const char *paths, ...)
100 {
101 va_list ap;
102 int length;
103 int i;
104 char *p;
105 char *path;
106 char *pathp;
107
108 if (! path1 || ! paths)
109 return (NULL);
110
111 length = strlen (path1) + strlen (paths) + 3;
112 i = 0;
113 va_start (ap, paths);
114 while ((p = va_arg (ap, char *)) != NULL)
115 length += strlen (p) + 1;
116 va_end (ap);
117
118 path = rc_xmalloc (length);
119 memset (path, 0, length);
120 memcpy (path, path1, strlen (path1));
121 pathp = path + strlen (path1) - 1;
122 if (*pathp != '/')
123 {
124 pathp++;
125 *pathp++ = '/';
126 }
127 else
128 pathp++;
129 memcpy (pathp, paths, strlen (paths));
130 pathp += strlen (paths);
131
132 va_start (ap, paths);
133 while ((p = va_arg (ap, char *)) != NULL)
134 {
135 if (*pathp != '/')
136 *pathp++ = '/';
137 i = strlen (p);
138 memcpy (pathp, p, i);
139 pathp += i;
140 }
141 va_end (ap);
142
143 *pathp++ = 0;
144
145 return (path);
146 }
147
148 bool rc_exists (const char *pathname)
149 {
150 struct stat buf;
151
152 if (! pathname)
153 return (false);
154
155 if (stat (pathname, &buf) == 0)
156 return (true);
157
158 errno = 0;
159 return (false);
160 }
161
162 bool rc_is_file (const char *pathname)
163 {
164 struct stat buf;
165
166 if (! pathname)
167 return (false);
168
169 if (stat (pathname, &buf) == 0)
170 return (S_ISREG (buf.st_mode));
171
172 errno = 0;
173 return (false);
174 }
175
176 bool rc_is_dir (const char *pathname)
177 {
178 struct stat buf;
179
180 if (! pathname)
181 return (false);
182
183 if (stat (pathname, &buf) == 0)
184 return (S_ISDIR (buf.st_mode));
185
186 errno = 0;
187 return (false);
188 }
189
190 bool rc_is_link (const char *pathname)
191 {
192 struct stat buf;
193
194 if (! pathname)
195 return (false);
196
197 if (lstat (pathname, &buf) == 0)
198 return (S_ISLNK (buf.st_mode));
199
200 errno = 0;
201 return (false);
202 }
203
204 bool rc_is_exec (const char *pathname)
205 {
206 struct stat buf;
207
208 if (! pathname)
209 return (false);
210
211 if (lstat (pathname, &buf) == 0)
212 return (buf.st_mode & S_IXUGO);
213
214 errno = 0;
215 return (false);
216 }
217
218 char **rc_ls_dir (char **list, const char *dir, int options)
219 {
220 DIR *dp;
221 struct dirent *d;
222
223 if (! dir)
224 return (list);
225
226 if ((dp = opendir (dir)) == NULL)
227 {
228 eerror ("failed to opendir `%s': %s", dir, strerror (errno));
229 return (list);
230 }
231
232 errno = 0;
233 while (((d = readdir (dp)) != NULL) && errno == 0)
234 {
235 if (d->d_name[0] != '.')
236 {
237 if (options & RC_LS_INITD)
238 {
239 int l = strlen (d->d_name);
240 char *init = rc_strcatpaths (RC_INITDIR, d->d_name, NULL);
241 bool ok = rc_exists (init);
242 free (init);
243 if (! ok)
244 continue;
245
246 /* .sh files are not init scripts */
247 if (l > 2 && d->d_name[l - 3] == '.' &&
248 d->d_name[l - 2] == 's' &&
249 d->d_name[l - 1] == 'h')
250 continue;
251 }
252 list = rc_strlist_addsort (list, d->d_name);
253 }
254 }
255 closedir (dp);
256
257 if (errno != 0)
258 {
259 eerror ("failed to readdir `%s': %s", dir, strerror (errno));
260 rc_strlist_free (list);
261 return (NULL);
262 }
263
264 return (list);
265 }
266
267 bool rc_rm_dir (const char *pathname, bool top)
268 {
269 DIR *dp;
270 struct dirent *d;
271
272 if (! pathname)
273 return (false);
274
275 if ((dp = opendir (pathname)) == NULL)
276 {
277 eerror ("failed to opendir `%s': %s", pathname, strerror (errno));
278 return (false);
279 }
280
281 errno = 0;
282 while (((d = readdir (dp)) != NULL) && errno == 0)
283 {
284 if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0)
285 {
286 char *tmp = rc_strcatpaths (pathname, d->d_name, NULL);
287 if (d->d_type == DT_DIR)
288 {
289 if (! rc_rm_dir (tmp, true))
290 {
291 free (tmp);
292 closedir (dp);
293 return (false);
294 }
295 }
296 else
297 {
298 if (unlink (tmp))
299 {
300 eerror ("failed to unlink `%s': %s", tmp, strerror (errno));
301 free (tmp);
302 closedir (dp);
303 return (false);
304 }
305 }
306 free (tmp);
307 }
308 }
309 if (errno != 0)
310 eerror ("failed to readdir `%s': %s", pathname, strerror (errno));
311 closedir (dp);
312
313 if (top && rmdir (pathname) != 0)
314 {
315 eerror ("failed to rmdir `%s': %s", pathname, strerror (errno));
316 return false;
317 }
318
319 return (true);
320 }
321
322 char **rc_get_config (char **list, const char *file)
323 {
324 FILE *fp;
325 char buffer[RC_LINEBUFFER];
326 char *p;
327 char *token;
328 char *line;
329 char *linep;
330 char *linetok;
331 int i = 0;
332 bool replaced;
333 char *entry;
334 char *newline;
335
336 if (! (fp = fopen (file, "r")))
337 {
338 ewarn ("load_config_file `%s': %s", file, strerror (errno));
339 return (list);
340 }
341
342 while (fgets (buffer, RC_LINEBUFFER, fp))
343 {
344 p = buffer;
345
346 /* Strip leading spaces/tabs */
347 while ((*p == ' ') || (*p == '\t'))
348 p++;
349
350 if (! p || strlen (p) < 3 || p[0] == '#')
351 continue;
352
353 /* Get entry */
354 token = strsep (&p, "=");
355 if (! token)
356 continue;
357
358 entry = rc_xstrdup (token);
359
360 do
361 {
362 /* Bash variables are usually quoted */
363 token = strsep (&p, "\"\'");
364 }
365 while ((token) && (strlen (token) == 0));
366
367 /* Drop a newline if that's all we have */
368 i = strlen (token) - 1;
369 if (token[i] == 10)
370 token[i] = 0;
371
372 i = strlen (entry) + strlen (token) + 2;
373 newline = rc_xmalloc (i);
374 snprintf (newline, i, "%s=%s", entry, token);
375
376 replaced = false;
377 /* In shells the last item takes precedence, so we need to remove
378 any prior values we may already have */
379 STRLIST_FOREACH (list, line, i)
380 {
381 char *tmp = rc_xstrdup (line);
382 linep = tmp;
383 linetok = strsep (&linep, "=");
384 if (strcmp (linetok, entry) == 0)
385 {
386 /* We have a match now - to save time we directly replace it */
387 free (list[i - 1]);
388 list[i - 1] = newline;
389 replaced = true;
390 free (tmp);
391 break;
392 }
393 free (tmp);
394 }
395
396 if (! replaced)
397 {
398 list = rc_strlist_addsort (list, newline);
399 free (newline);
400 }
401 free (entry);
402 }
403 fclose (fp);
404
405 return (list);
406 }
407
408 char *rc_get_config_entry (char **list, const char *entry)
409 {
410 char *line;
411 int i;
412 char *p;
413
414 STRLIST_FOREACH (list, line, i)
415 {
416 p = strchr (line, '=');
417 if (p && strncmp (entry, line, p - line) == 0)
418 return (p += 1);
419 }
420
421 return (NULL);
422 }
423
424 char **rc_get_list (char **list, const char *file)
425 {
426 FILE *fp;
427 char buffer[RC_LINEBUFFER];
428 char *p;
429 char *token;
430
431 if (! (fp = fopen (file, "r")))
432 {
433 ewarn ("rc_get_list `%s': %s", file, strerror (errno));
434 return (list);
435 }
436
437 while (fgets (buffer, RC_LINEBUFFER, fp))
438 {
439 p = buffer;
440
441 /* Strip leading spaces/tabs */
442 while ((*p == ' ') || (*p == '\t'))
443 p++;
444
445 /* Get entry - we do not want comments */
446 token = strsep (&p, "#");
447 if (token && (strlen (token) > 1))
448 {
449 token[strlen (token) - 1] = 0;
450 list = rc_strlist_add (list, token);
451 }
452 }
453 fclose (fp);
454
455 return (list);
456 }
457
458 char **rc_filter_env (void)
459 {
460 char **env = NULL;
461 char **whitelist = NULL;
462 char *env_name = NULL;
463 char **profile = NULL;
464 int count = 0;
465 bool got_path = false;
466 char *env_var;
467 int env_len;
468 char *p;
469 char *token;
470 char *sep;
471 char *e;
472 int pplen = strlen (PATH_PREFIX);
473
474 whitelist = rc_get_list (whitelist, SYS_WHITELIST);
475 if (! whitelist)
476 ewarn ("system environment whitelist (" SYS_WHITELIST ") missing");
477
478 whitelist = rc_get_list (whitelist, USR_WHITELIST);
479
480 if (! whitelist)
481 return (NULL);
482
483 if (rc_is_file (PROFILE_ENV))
484 profile = rc_get_config (profile, PROFILE_ENV);
485
486 STRLIST_FOREACH (whitelist, env_name, count)
487 {
488 char *space = strchr (env_name, ' ');
489 if (space)
490 *space = 0;
491
492 env_var = getenv (env_name);
493
494 if (! env_var && profile)
495 {
496 env_len = strlen (env_name) + strlen ("export ") + 1;
497 p = rc_xmalloc (sizeof (char *) * env_len);
498 snprintf (p, env_len, "export %s", env_name);
499 env_var = rc_get_config_entry (profile, p);
500 free (p);
501 }
502
503 if (! env_var)
504 continue;
505
506 /* Ensure our PATH is prefixed with the system locations first
507 for a little extra security */
508 if (strcmp (env_name, "PATH") == 0 &&
509 strncmp (PATH_PREFIX, env_var, pplen) != 0)
510 {
511 got_path = true;
512 env_len = strlen (env_name) + strlen (env_var) + pplen + 2;
513 e = p = rc_xmalloc (sizeof (char *) * env_len);
514 p += sprintf (e, "%s=%s", env_name, PATH_PREFIX);
515
516 /* Now go through the env var and only add bits not in our PREFIX */
517 sep = env_var;
518 while ((token = strsep (&sep, ":")))
519 {
520 char *np = strdup (PATH_PREFIX);
521 char *npp = np;
522 char *tok = NULL;
523 while ((tok = strsep (&npp, ":")))
524 if (strcmp (tok, token) == 0)
525 break;
526 if (! tok)
527 p += sprintf (p, ":%s", token);
528 free (np);
529 }
530 *p++ = 0;
531 }
532 else
533 {
534 env_len = strlen (env_name) + strlen (env_var) + 2;
535 e = rc_xmalloc (sizeof (char *) * env_len);
536 snprintf (e, env_len, "%s=%s", env_name, env_var);
537 }
538
539 env = rc_strlist_add (env, e);
540 free (e);
541 }
542
543 /* We filtered the env but didn't get a PATH? Very odd.
544 However, we do need a path, so use a default. */
545 if (! got_path)
546 {
547 env_len = strlen ("PATH=") + strlen (PATH_PREFIX) + 2;
548 p = rc_xmalloc (sizeof (char *) * env_len);
549 snprintf (p, env_len, "PATH=%s", PATH_PREFIX);
550 env = rc_strlist_add (env, p);
551 free (p);
552 }
553
554 rc_strlist_free (whitelist);
555 rc_strlist_free (profile);
556
557 return (env);
558 }
559
560 /* Other systems may need this at some point, but for now it's Linux only */
561 #ifdef __linux__
562 static bool file_regex (const char *file, const char *regex)
563 {
564 FILE *fp;
565 char buffer[RC_LINEBUFFER];
566 regex_t re;
567 bool retval = false;
568 int result;
569
570 if (! rc_exists (file))
571 return (false);
572
573 if (! (fp = fopen (file, "r")))
574 {
575 ewarn ("file_regex `%s': %s", file, strerror (errno));
576 return (false);
577 }
578
579 if ((result = regcomp (&re, regex, REG_EXTENDED | REG_NOSUB)) != 0)
580 {
581 fclose (fp);
582 regerror (result, &re, buffer, sizeof (buffer));
583 eerror ("file_regex: %s", buffer);
584 return (false);
585 }
586
587 while (fgets (buffer, RC_LINEBUFFER, fp))
588 {
589 if (regexec (&re, buffer, 0, NULL, 0) == 0)
590 {
591 retval = true;
592 break;
593 }
594 }
595 fclose (fp);
596 regfree (&re);
597
598 return (retval);
599 }
600 #endif
601
602 char **rc_config_env (char **env)
603 {
604 char *line;
605 int i;
606 char *p;
607 char **config = rc_get_config (NULL, RC_CONFIG);
608 char *e;
609 char sys[6];
610 struct utsname uts;
611 bool has_net_fs_list = false;
612 FILE *fp;
613 char buffer[PATH_MAX];
614
615 STRLIST_FOREACH (config, line, i)
616 {
617 p = strchr (line, '=');
618 if (! p)
619 continue;
620
621 *p = 0;
622 e = getenv (line);
623 if (! e)
624 {
625 *p = '=';
626 env = rc_strlist_add (env, line);
627 }
628 else
629 {
630 int len = strlen (line) + strlen (e) + 2;
631 char *new = rc_xmalloc (sizeof (char *) * len);
632 snprintf (new, len, "%s=%s", line, e);
633 env = rc_strlist_add (env, new);
634 free (new);
635 }
636 }
637 rc_strlist_free (config);
638
639 i = strlen ("RC_LIBDIR=//rcscripts") + strlen (LIBDIR) + 2;
640 line = rc_xmalloc (sizeof (char *) * i);
641 snprintf (line, i, "RC_LIBDIR=/" LIBDIR "/rcscripts");
642 env = rc_strlist_add (env, line);
643 free (line);
644
645 i += strlen ("/init.d");
646 line = rc_xmalloc (sizeof (char *) * i);
647 snprintf (line, i, "RC_SVCDIR=/" LIBDIR "/rcscripts/init.d");
648 env = rc_strlist_add (env, line);
649 free (line);
650
651 env = rc_strlist_add (env, "RC_BOOTLEVEL=" RC_LEVEL_BOOT);
652
653 p = rc_get_runlevel ();
654 i = strlen ("RC_SOFTLEVEL=") + strlen (p) + 1;
655 line = rc_xmalloc (sizeof (char *) * i);
656 snprintf (line, i, "RC_SOFTLEVEL=%s", p);
657 env = rc_strlist_add (env, line);
658 free (line);
659
660 if (rc_exists (RC_SVCDIR "ksoftlevel"))
661 {
662 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
663 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
664 strerror (errno));
665 else
666 {
667 memset (buffer, 0, sizeof (buffer));
668 if (fgets (buffer, sizeof (buffer), fp))
669 {
670 i = strlen (buffer) - 1;
671 if (buffer[i] == '\n')
672 buffer[i] = 0;
673 i += strlen ("RC_DEFAULTLEVEL=") + 2;
674 line = rc_xmalloc (sizeof (char *) * i);
675 snprintf (line, i, "RC_DEFAULTLEVEL=%s", buffer);
676 env = rc_strlist_add (env, line);
677 free (line);
678 }
679 fclose (fp);
680 }
681 }
682 else
683 env = rc_strlist_add (env, "RC_DEFAULTLEVEL=" RC_LEVEL_DEFAULT);
684
685 memset (sys, 0, sizeof (sys));
686
687 /* Linux can run some funky stuff like Xen, VServer, UML, etc
688 We store this special system in RC_SYS so our scripts run fast */
689 #ifdef __linux__
690 if (rc_is_dir ("/proc/xen"))
691 {
692 fp = fopen ("/proc/xen/capabilities", "r");
693 if (fp)
694 {
695 fclose (fp);
696 if (file_regex ("/proc/xen/capabilities", "control_d"))
697 sprintf (sys, "XENU");
698 }
699 if (! sys)
700 sprintf (sys, "XEN0");
701 }
702 else if (file_regex ("/proc/cpuinfo", "UML"))
703 sprintf (sys, "UML");
704 else if (file_regex ("/proc/self/status",
705 "(s_context|VxID|envID):[[:space:]]*[1-9]"))
706 sprintf(sys, "VPS");
707 #endif
708
709 /* Only add a NET_FS list if not defined */
710 STRLIST_FOREACH (env, line, i)
711 if (strncmp (line, "RC_NET_FS_LIST=", strlen ("RC_NET_FS_LIST=")) == 0)
712 {
713 has_net_fs_list = true;
714 break;
715 }
716 if (! has_net_fs_list)
717 {
718 i = strlen ("RC_NET_FS_LIST=") + strlen (RC_NET_FS_LIST_DEFAULT) + 1;
719 line = rc_xmalloc (sizeof (char *) * i);
720 snprintf (line, i, "RC_NET_FS_LIST=%s", RC_NET_FS_LIST_DEFAULT);
721 env = rc_strlist_add (env, line);
722 free (line);
723 }
724
725 if (sys[0])
726 {
727 i = strlen ("RC_SYS=") + strlen (sys) + 2;
728 line = rc_xmalloc (sizeof (char *) * i);
729 snprintf (line, i, "RC_SYS=%s", sys);
730 env = rc_strlist_add (env, line);
731 free (line);
732 }
733
734 /* Some scripts may need to take a different code path if Linux/FreeBSD, etc
735 To save on calling uname, we store it in an environment variable */
736 if (uname (&uts) == 0)
737 {
738 i = strlen ("RC_UNAME=") + strlen (uts.sysname) + 2;
739 line = rc_xmalloc (sizeof (char *) * i);
740 snprintf (line, i, "RC_UNAME=%s", uts.sysname);
741 env = rc_strlist_add (env, line);
742 free (line);
743 }
744
745 /* Set this var to ensure that things are POSIX, which makes scripts work
746 on non GNU systems with less effort. */
747 env = rc_strlist_add (env, "POSIXLY_CORRECT=1");
748
749 return (env);
750 }

  ViewVC Help
Powered by ViewVC 1.1.20