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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2550 - (show annotations) (download) (as text)
Fri Apr 6 01:04:07 2007 UTC (8 years ago) by uberlord
File MIME type: text/x-csrc
File size: 15689 byte(s)
Misc fixes, plugged a memory leak in runscript.c and use va_copy to avoid nasty segfaults
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,
241 (char *) NULL);
242 bool ok = rc_exists (init);
243 free (init);
244 if (! ok)
245 continue;
246
247 /* .sh files are not init scripts */
248 if (l > 2 && d->d_name[l - 3] == '.' &&
249 d->d_name[l - 2] == 's' &&
250 d->d_name[l - 1] == 'h')
251 continue;
252 }
253 list = rc_strlist_addsort (list, d->d_name);
254 }
255 }
256 closedir (dp);
257
258 if (errno != 0)
259 {
260 eerror ("failed to readdir `%s': %s", dir, strerror (errno));
261 rc_strlist_free (list);
262 return (NULL);
263 }
264
265 return (list);
266 }
267
268 bool rc_rm_dir (const char *pathname, bool top)
269 {
270 DIR *dp;
271 struct dirent *d;
272
273 if (! pathname)
274 return (false);
275
276 if ((dp = opendir (pathname)) == NULL)
277 {
278 eerror ("failed to opendir `%s': %s", pathname, strerror (errno));
279 return (false);
280 }
281
282 errno = 0;
283 while (((d = readdir (dp)) != NULL) && errno == 0)
284 {
285 if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0)
286 {
287 char *tmp = rc_strcatpaths (pathname, d->d_name, (char *) NULL);
288 if (d->d_type == DT_DIR)
289 {
290 if (! rc_rm_dir (tmp, true))
291 {
292 free (tmp);
293 closedir (dp);
294 return (false);
295 }
296 }
297 else
298 {
299 if (unlink (tmp))
300 {
301 eerror ("failed to unlink `%s': %s", tmp, strerror (errno));
302 free (tmp);
303 closedir (dp);
304 return (false);
305 }
306 }
307 free (tmp);
308 }
309 }
310 if (errno != 0)
311 eerror ("failed to readdir `%s': %s", pathname, strerror (errno));
312 closedir (dp);
313
314 if (top && rmdir (pathname) != 0)
315 {
316 eerror ("failed to rmdir `%s': %s", pathname, strerror (errno));
317 return false;
318 }
319
320 return (true);
321 }
322
323 char **rc_get_config (char **list, const char *file)
324 {
325 FILE *fp;
326 char buffer[RC_LINEBUFFER];
327 char *p;
328 char *token;
329 char *line;
330 char *linep;
331 char *linetok;
332 int i = 0;
333 bool replaced;
334 char *entry;
335 char *newline;
336
337 if (! (fp = fopen (file, "r")))
338 {
339 ewarn ("load_config_file `%s': %s", file, strerror (errno));
340 return (list);
341 }
342
343 while (fgets (buffer, RC_LINEBUFFER, fp))
344 {
345 p = buffer;
346
347 /* Strip leading spaces/tabs */
348 while ((*p == ' ') || (*p == '\t'))
349 p++;
350
351 if (! p || strlen (p) < 3 || p[0] == '#')
352 continue;
353
354 /* Get entry */
355 token = strsep (&p, "=");
356 if (! token)
357 continue;
358
359 entry = rc_xstrdup (token);
360
361 do
362 {
363 /* Bash variables are usually quoted */
364 token = strsep (&p, "\"\'");
365 }
366 while ((token) && (strlen (token) == 0));
367
368 /* Drop a newline if that's all we have */
369 i = strlen (token) - 1;
370 if (token[i] == 10)
371 token[i] = 0;
372
373 i = strlen (entry) + strlen (token) + 2;
374 newline = rc_xmalloc (i);
375 snprintf (newline, i, "%s=%s", entry, token);
376
377 replaced = false;
378 /* In shells the last item takes precedence, so we need to remove
379 any prior values we may already have */
380 STRLIST_FOREACH (list, line, i)
381 {
382 char *tmp = rc_xstrdup (line);
383 linep = tmp;
384 linetok = strsep (&linep, "=");
385 if (strcmp (linetok, entry) == 0)
386 {
387 /* We have a match now - to save time we directly replace it */
388 free (list[i - 1]);
389 list[i - 1] = newline;
390 replaced = true;
391 free (tmp);
392 break;
393 }
394 free (tmp);
395 }
396
397 if (! replaced)
398 {
399 list = rc_strlist_addsort (list, newline);
400 free (newline);
401 }
402 free (entry);
403 }
404 fclose (fp);
405
406 return (list);
407 }
408
409 char *rc_get_config_entry (char **list, const char *entry)
410 {
411 char *line;
412 int i;
413 char *p;
414
415 STRLIST_FOREACH (list, line, i)
416 {
417 p = strchr (line, '=');
418 if (p && strncmp (entry, line, p - line) == 0)
419 return (p += 1);
420 }
421
422 return (NULL);
423 }
424
425 char **rc_get_list (char **list, const char *file)
426 {
427 FILE *fp;
428 char buffer[RC_LINEBUFFER];
429 char *p;
430 char *token;
431
432 if (! (fp = fopen (file, "r")))
433 {
434 ewarn ("rc_get_list `%s': %s", file, strerror (errno));
435 return (list);
436 }
437
438 while (fgets (buffer, RC_LINEBUFFER, fp))
439 {
440 p = buffer;
441
442 /* Strip leading spaces/tabs */
443 while ((*p == ' ') || (*p == '\t'))
444 p++;
445
446 /* Get entry - we do not want comments */
447 token = strsep (&p, "#");
448 if (token && (strlen (token) > 1))
449 {
450 token[strlen (token) - 1] = 0;
451 list = rc_strlist_add (list, token);
452 }
453 }
454 fclose (fp);
455
456 return (list);
457 }
458
459 char **rc_filter_env (void)
460 {
461 char **env = NULL;
462 char **whitelist = NULL;
463 char *env_name = NULL;
464 char **profile = NULL;
465 int count = 0;
466 bool got_path = false;
467 char *env_var;
468 int env_len;
469 char *p;
470 char *token;
471 char *sep;
472 char *e;
473 int pplen = strlen (PATH_PREFIX);
474
475 whitelist = rc_get_list (whitelist, SYS_WHITELIST);
476 if (! whitelist)
477 ewarn ("system environment whitelist (" SYS_WHITELIST ") missing");
478
479 whitelist = rc_get_list (whitelist, USR_WHITELIST);
480
481 if (! whitelist)
482 return (NULL);
483
484 if (rc_is_file (PROFILE_ENV))
485 profile = rc_get_config (profile, PROFILE_ENV);
486
487 STRLIST_FOREACH (whitelist, env_name, count)
488 {
489 char *space = strchr (env_name, ' ');
490 if (space)
491 *space = 0;
492
493 env_var = getenv (env_name);
494
495 if (! env_var && profile)
496 {
497 env_len = strlen (env_name) + strlen ("export ") + 1;
498 p = rc_xmalloc (sizeof (char *) * env_len);
499 snprintf (p, env_len, "export %s", env_name);
500 env_var = rc_get_config_entry (profile, p);
501 free (p);
502 }
503
504 if (! env_var)
505 continue;
506
507 /* Ensure our PATH is prefixed with the system locations first
508 for a little extra security */
509 if (strcmp (env_name, "PATH") == 0 &&
510 strncmp (PATH_PREFIX, env_var, pplen) != 0)
511 {
512 got_path = true;
513 env_len = strlen (env_name) + strlen (env_var) + pplen + 2;
514 e = p = rc_xmalloc (sizeof (char *) * env_len);
515 p += sprintf (e, "%s=%s", env_name, PATH_PREFIX);
516
517 /* Now go through the env var and only add bits not in our PREFIX */
518 sep = env_var;
519 while ((token = strsep (&sep, ":")))
520 {
521 char *np = strdup (PATH_PREFIX);
522 char *npp = np;
523 char *tok = NULL;
524 while ((tok = strsep (&npp, ":")))
525 if (strcmp (tok, token) == 0)
526 break;
527 if (! tok)
528 p += sprintf (p, ":%s", token);
529 free (np);
530 }
531 *p++ = 0;
532 }
533 else
534 {
535 env_len = strlen (env_name) + strlen (env_var) + 2;
536 e = rc_xmalloc (sizeof (char *) * env_len);
537 snprintf (e, env_len, "%s=%s", env_name, env_var);
538 }
539
540 env = rc_strlist_add (env, e);
541 free (e);
542 }
543
544 /* We filtered the env but didn't get a PATH? Very odd.
545 However, we do need a path, so use a default. */
546 if (! got_path)
547 {
548 env_len = strlen ("PATH=") + strlen (PATH_PREFIX) + 2;
549 p = rc_xmalloc (sizeof (char *) * env_len);
550 snprintf (p, env_len, "PATH=%s", PATH_PREFIX);
551 env = rc_strlist_add (env, p);
552 free (p);
553 }
554
555 rc_strlist_free (whitelist);
556 rc_strlist_free (profile);
557
558 return (env);
559 }
560
561 /* Other systems may need this at some point, but for now it's Linux only */
562 #ifdef __linux__
563 static bool file_regex (const char *file, const char *regex)
564 {
565 FILE *fp;
566 char buffer[RC_LINEBUFFER];
567 regex_t re;
568 bool retval = false;
569 int result;
570
571 if (! rc_exists (file))
572 return (false);
573
574 if (! (fp = fopen (file, "r")))
575 {
576 ewarn ("file_regex `%s': %s", file, strerror (errno));
577 return (false);
578 }
579
580 if ((result = regcomp (&re, regex, REG_EXTENDED | REG_NOSUB)) != 0)
581 {
582 fclose (fp);
583 regerror (result, &re, buffer, sizeof (buffer));
584 eerror ("file_regex: %s", buffer);
585 return (false);
586 }
587
588 while (fgets (buffer, RC_LINEBUFFER, fp))
589 {
590 if (regexec (&re, buffer, 0, NULL, 0) == 0)
591 {
592 retval = true;
593 break;
594 }
595 }
596 fclose (fp);
597 regfree (&re);
598
599 return (retval);
600 }
601 #endif
602
603 char **rc_config_env (char **env)
604 {
605 char *line;
606 int i;
607 char *p;
608 char **config = rc_get_config (NULL, RC_CONFIG);
609 char *e;
610 char sys[6];
611 struct utsname uts;
612 bool has_net_fs_list = false;
613 FILE *fp;
614 char buffer[PATH_MAX];
615
616 STRLIST_FOREACH (config, line, i)
617 {
618 p = strchr (line, '=');
619 if (! p)
620 continue;
621
622 *p = 0;
623 e = getenv (line);
624 if (! e)
625 {
626 *p = '=';
627 env = rc_strlist_add (env, line);
628 }
629 else
630 {
631 int len = strlen (line) + strlen (e) + 2;
632 char *new = rc_xmalloc (sizeof (char *) * len);
633 snprintf (new, len, "%s=%s", line, e);
634 env = rc_strlist_add (env, new);
635 free (new);
636 }
637 }
638 rc_strlist_free (config);
639
640 i = strlen ("RC_LIBDIR=//rcscripts") + strlen (LIBDIR) + 2;
641 line = rc_xmalloc (sizeof (char *) * i);
642 snprintf (line, i, "RC_LIBDIR=/" LIBDIR "/rcscripts");
643 env = rc_strlist_add (env, line);
644 free (line);
645
646 i += strlen ("/init.d");
647 line = rc_xmalloc (sizeof (char *) * i);
648 snprintf (line, i, "RC_SVCDIR=/" LIBDIR "/rcscripts/init.d");
649 env = rc_strlist_add (env, line);
650 free (line);
651
652 env = rc_strlist_add (env, "RC_BOOTLEVEL=" RC_LEVEL_BOOT);
653
654 p = rc_get_runlevel ();
655 i = strlen ("RC_SOFTLEVEL=") + strlen (p) + 1;
656 line = rc_xmalloc (sizeof (char *) * i);
657 snprintf (line, i, "RC_SOFTLEVEL=%s", p);
658 env = rc_strlist_add (env, line);
659 free (line);
660
661 if (rc_exists (RC_SVCDIR "ksoftlevel"))
662 {
663 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
664 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
665 strerror (errno));
666 else
667 {
668 memset (buffer, 0, sizeof (buffer));
669 if (fgets (buffer, sizeof (buffer), fp))
670 {
671 i = strlen (buffer) - 1;
672 if (buffer[i] == '\n')
673 buffer[i] = 0;
674 i += strlen ("RC_DEFAULTLEVEL=") + 2;
675 line = rc_xmalloc (sizeof (char *) * i);
676 snprintf (line, i, "RC_DEFAULTLEVEL=%s", buffer);
677 env = rc_strlist_add (env, line);
678 free (line);
679 }
680 fclose (fp);
681 }
682 }
683 else
684 env = rc_strlist_add (env, "RC_DEFAULTLEVEL=" RC_LEVEL_DEFAULT);
685
686 memset (sys, 0, sizeof (sys));
687
688 /* Linux can run some funky stuff like Xen, VServer, UML, etc
689 We store this special system in RC_SYS so our scripts run fast */
690 #ifdef __linux__
691 if (rc_is_dir ("/proc/xen"))
692 {
693 fp = fopen ("/proc/xen/capabilities", "r");
694 if (fp)
695 {
696 fclose (fp);
697 if (file_regex ("/proc/xen/capabilities", "control_d"))
698 snprintf (sys, sizeof (sys), "XENU");
699 }
700 if (! sys)
701 snprintf (sys, sizeof (sys), "XEN0");
702 }
703 else if (file_regex ("/proc/cpuinfo", "UML"))
704 snprintf (sys, sizeof (sys), "UML");
705 else if (file_regex ("/proc/self/status",
706 "(s_context|VxID|envID):[[:space:]]*[1-9]"))
707 snprintf (sys, sizeof (sys), "VPS");
708 #endif
709
710 /* Only add a NET_FS list if not defined */
711 STRLIST_FOREACH (env, line, i)
712 if (strncmp (line, "RC_NET_FS_LIST=", strlen ("RC_NET_FS_LIST=")) == 0)
713 {
714 has_net_fs_list = true;
715 break;
716 }
717 if (! has_net_fs_list)
718 {
719 i = strlen ("RC_NET_FS_LIST=") + strlen (RC_NET_FS_LIST_DEFAULT) + 1;
720 line = rc_xmalloc (sizeof (char *) * i);
721 snprintf (line, i, "RC_NET_FS_LIST=%s", RC_NET_FS_LIST_DEFAULT);
722 env = rc_strlist_add (env, line);
723 free (line);
724 }
725
726 if (sys[0])
727 {
728 i = strlen ("RC_SYS=") + strlen (sys) + 2;
729 line = rc_xmalloc (sizeof (char *) * i);
730 snprintf (line, i, "RC_SYS=%s", sys);
731 env = rc_strlist_add (env, line);
732 free (line);
733 }
734
735 /* Some scripts may need to take a different code path if Linux/FreeBSD, etc
736 To save on calling uname, we store it in an environment variable */
737 if (uname (&uts) == 0)
738 {
739 i = strlen ("RC_UNAME=") + strlen (uts.sysname) + 2;
740 line = rc_xmalloc (sizeof (char *) * i);
741 snprintf (line, i, "RC_UNAME=%s", uts.sysname);
742 env = rc_strlist_add (env, line);
743 free (line);
744 }
745
746 /* Set this var to ensure that things are POSIX, which makes scripts work
747 on non GNU systems with less effort. */
748 env = rc_strlist_add (env, "POSIXLY_CORRECT=1");
749
750 return (env);
751 }

  ViewVC Help
Powered by ViewVC 1.1.20