1 /* -*- c-file-style: "java"; indent-tabs-mode: nil; tab-width: 4; fill-column: 78 -*-
2  *
3  * distcc -- A simple distributed compiler system
4  *
5  * Copyright (C) 2002, 2003 by Martin Pool <mbp@samba.org>
6  * Copyright 2007 Google Inc.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21  * USA.
22  */
23 
24 #include <config.h>
25 
26 #include <dirent.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <signal.h>
34 #include <netdb.h>
35 #include <ctype.h>
36 
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/un.h>
40 #include <sys/socket.h>
41 
42 #include <netinet/in.h>
43 
44 #ifdef HAVE_SYS_RESOURCE_H
45 #include <sys/resource.h>
46 #endif
47 
48 #ifdef HAVE_SYS_LOADAVG_H
49 #include <sys/loadavg.h>
50 #endif
51 
52 #include <sys/param.h>
53 
54 #include "distcc.h"
55 #include "trace.h"
56 #include "util.h"
57 #include "exitcode.h"
58 #include "snprintf.h"
59 
60             /* I will make a man more precious than fine
61              * gold; even a man than the golden wedge of
62              * Ophir.
63              *        -- Isaiah 13:12 */
64 
65 
dcc_exit(int exitcode)66 void dcc_exit(int exitcode)
67 {
68     struct rusage self_ru, children_ru;
69 
70     if (getrusage(RUSAGE_SELF, &self_ru)) {
71         rs_log_warning("getrusage(RUSAGE_SELF) failed: %s", strerror(errno));
72         memset(&self_ru, 0, sizeof self_ru);
73     }
74     if (getrusage(RUSAGE_CHILDREN, &children_ru)) {
75         rs_log_warning("getrusage(RUSAGE_CHILDREN) failed: %s", strerror(errno));
76         memset(&children_ru, 0, sizeof children_ru);
77     }
78 
79     /* NB fields must match up for microseconds */
80     rs_log(RS_LOG_INFO,
81            "exit: code %d; self: %d.%06d user %d.%06d sys; children: %d.%06d user %d.%06d sys",
82            exitcode,
83            (int) self_ru.ru_utime.tv_sec, (int) self_ru.ru_utime.tv_usec,
84            (int) self_ru.ru_stime.tv_sec, (int) self_ru.ru_stime.tv_usec,
85            (int) children_ru.ru_utime.tv_sec, (int) children_ru.ru_utime.tv_usec,
86            (int) children_ru.ru_stime.tv_sec, (int)  children_ru.ru_stime.tv_usec);
87 
88     exit(exitcode);
89 }
90 
91 
str_endswith(const char * tail,const char * tiger)92 int str_endswith(const char *tail, const char *tiger)
93 {
94     size_t len_tail = strlen(tail);
95     size_t len_tiger = strlen(tiger);
96 
97     if (len_tail > len_tiger)
98         return 0;
99 
100     return !strcmp(tiger + len_tiger - len_tail, tail);
101 }
102 
103 
str_startswith(const char * head,const char * worm)104 int str_startswith(const char *head, const char *worm)
105 {
106     return !strncmp(head, worm, strlen(head));
107 }
108 
109 
110 
111 /**
112  * Skim through NULL-terminated @p argv, looking for @p s.
113  **/
argv_contains(char ** argv,const char * s)114 int argv_contains(char **argv, const char *s)
115 {
116     while (*argv) {
117         if (!strcmp(*argv, s))
118             return 1;
119         argv++;
120     }
121     return 0;
122 }
123 
124 
125 /**
126  * Redirect a file descriptor into (or out of) a file.
127  *
128  * Used, for example, to catch compiler error messages into a
129  * temporary file.
130  **/
dcc_redirect_fd(int fd,const char * fname,int mode)131 int dcc_redirect_fd(int fd, const char *fname, int mode)
132 {
133     int newfd;
134 
135     /* ignore errors */
136     close(fd);
137 
138     newfd = open(fname, mode, 0666);
139     if (newfd == -1) {
140         rs_log_crit("failed to reopen fd%d onto %s: %s",
141                     fd, fname, strerror(errno));
142         return EXIT_IO_ERROR;
143     } else if (newfd != fd) {
144         rs_log_crit("oops, reopened fd%d onto fd%d?", fd, newfd);
145         return EXIT_IO_ERROR;
146     }
147 
148     return 0;
149 }
150 
151 
152 
dcc_gethostname(void)153 char *dcc_gethostname(void)
154 {
155     static char myname[100] = "\0";
156 
157     if (!myname[0]) {
158         if (gethostname(myname, sizeof myname - 1) == -1)
159             strcpy(myname, "UNKNOWN");
160     }
161 
162     return myname;
163 }
164 
165 
166 /**
167  * Look up a boolean environment option, which must be either "0" or
168  * "1".  The default, if it's not set or is empty, is @p default.
169  **/
dcc_getenv_bool(const char * name,int default_value)170 int dcc_getenv_bool(const char *name, int default_value)
171 {
172     const char *e;
173 
174     e = getenv(name);
175     if (!e || !*e)
176         return default_value;
177     if (!strcmp(e, "1"))
178         return 1;
179     else if (!strcmp(e, "0"))
180         return 0;
181     else
182         return default_value;
183 }
184 
185 
186 #define IS_LEGAL_DOMAIN_CHAR(c) (isalnum((uint8_t)c) || ((c) == '-') || ((c) == '.'))
187 
188 /* Copy domain part of hostname to static buffer.
189  * If hostname has no domain part, returns -1.
190  * If domain lookup fails, returns -1.
191  * Otherwise places pointer to domain in *domain_name and returns 0.
192  *
193  * This should yield the same result as the linux command
194  * 'dnsdomainname' or 'hostname -d'.
195  **/
dcc_get_dns_domain(const char ** domain_name)196 int dcc_get_dns_domain(const char **domain_name)
197 {
198 #if 0 /* Too expensive */
199 
200     static char host_name[1024];
201     struct hostent *h;
202     int ret;
203 
204     ret = gethostname(host_name, sizeof(host_name));
205     if (ret != 0)
206         return -1;
207 
208     h = gethostbyname(host_name);
209     if (h == NULL) {
210         rs_log_error("failed to look up self \"%s\": %s", host_name,
211                      hstrerror(h_errno));
212         return -1;
213     }
214 
215     strncpy(host_name, h->h_name, sizeof(host_name) - 1);
216     host_name[sizeof(host_name) - 1] = '\0';
217     *domain_name = strchr(h->h_name, '.');
218 
219 #else  /* cheaper */
220     const char *envh, *envh2;
221     int i;
222     const int MAXDOMAINLEN = 512;
223 
224     /* Kludge for speed: Try to retrieve FQDN from environment.
225      * This can save many milliseconds on a network that's busy and lossy
226      * (glibc retries DNS operations very slowly).
227      */
228 
229     /* Solaris, BSD tend to put it in HOST.
230      * (Some flavors of Linux put the non-qualified hostname in HOST,
231      *  so ignore this if it doesn't have a dot in it.)
232      */
233     envh = getenv("HOST");
234     if (envh && !strchr(envh, '.'))
235         envh = NULL;
236 
237     /* Some flavors of Linux put the FQDN in HOSTNAME when
238      * logged in interactively, but not when ssh'd in noninteractively.
239      * Ubuntu's bash puts it in HOSTNAME but doesn't export it!
240      */
241     envh2 = getenv("HOSTNAME");
242     if (envh2 && !strchr(envh2, '.'))
243         envh2 = NULL;
244 
245     /* Pick the 'better' of the two.  Longer is usually better. */
246     if (envh2 && (!envh || (strlen(envh) < strlen(envh2))))
247         envh = envh2;
248 
249     /* If the above didn't work out, fall back to the real way. */
250     if (!envh || !strchr(envh, '.')) {
251         static char host_name[1024];
252         struct hostent *h;
253         int ret;
254 
255         ret = gethostname(host_name, sizeof(host_name));
256         if (ret != 0)
257             return -1;
258 
259         /* If hostname has a dot in it, assume it's the DNS address */
260         if (!strchr(host_name, '.')) {
261             /* Otherwise ask DNS what our full hostname is */
262             h = gethostbyname(host_name);
263             if (h == NULL) {
264                 rs_log_error("failed to look up self \"%s\": %s", host_name,
265                              hstrerror(h_errno));
266                 return -1;
267             }
268             strncpy(host_name, h->h_name, sizeof(host_name) - 1);
269             host_name[sizeof(host_name) - 1] = '\0';
270         }
271         envh = host_name;
272     }
273 
274     /* validate to avoid possible errors from bad chars or huge value */
275     for (i=0; envh[i] != '\0'; i++) {
276         if (i > MAXDOMAINLEN || !IS_LEGAL_DOMAIN_CHAR(envh[i])) {
277             rs_log_error("HOST/HOSTNAME present in environment but illegal: '%s'", envh);
278             return -1;
279         }
280     }
281     *domain_name = strchr(envh, '.');
282 #endif
283 
284     if (*domain_name == NULL)
285         return -1;
286 
287     (*domain_name)++;
288     /* Return 0 on success, or -1 if the domain name is illegal, e.g. empty */
289     return ((*domain_name)[0] == '\0') ? -1 : 0;
290 }
291 
292 
293 
294 /**
295  * Set the `FD_CLOEXEC' flag of DESC if VALUE is nonzero,
296  * or clear the flag if VALUE is 0.
297  *
298  * From the GNU C Library examples.
299  *
300  * @returns 0 on success, or -1 on error with `errno' set.
301  **/
set_cloexec_flag(int desc,int value)302 int set_cloexec_flag (int desc, int value)
303 {
304     int oldflags = fcntl (desc, F_GETFD, 0);
305     /* If reading the flags failed, return error indication now. */
306     if (oldflags < 0)
307         return oldflags;
308     /* Set just the flag we want to set. */
309     if (value != 0)
310         oldflags |= FD_CLOEXEC;
311     else
312         oldflags &= ~FD_CLOEXEC;
313     /* Store modified flag word in the descriptor. */
314     return fcntl (desc, F_SETFD, oldflags);
315 }
316 
317 
318 /**
319  * Ignore or unignore SIGPIPE.
320  *
321  * The server and child ignore it, because distcc code wants to see
322  * EPIPE errors if something goes wrong.  However, for invoked
323  * children it is set back to the default value, because they may not
324  * handle the error properly.
325  **/
dcc_ignore_sigpipe(int val)326 int dcc_ignore_sigpipe(int val)
327 {
328     if (signal(SIGPIPE, val ? SIG_IGN : SIG_DFL) == SIG_ERR) {
329         rs_log_warning("signal(SIGPIPE, %s) failed: %s",
330                        val ? "ignore" : "default",
331                        strerror(errno));
332         return EXIT_DISTCC_FAILED;
333     }
334     return 0;
335 }
336 
337 /**
338  * Search through the $PATH looking for a directory containing a file called
339  * @p compiler_name, which is a symbolic link containing the string "distcc".
340  *
341  * Trim the path to just after the *last* such directory.
342  *
343  * If we find a distcc masquerade dir on the PATH, remove all the dirs up
344  * to that point.
345  **/
dcc_trim_path(const char * compiler_name)346 int dcc_trim_path(const char *compiler_name)
347 {
348     const char *envpath, *newpath, *p, *n;
349     char linkbuf[MAXPATHLEN], *buf;
350     struct stat sb;
351     size_t len;
352 
353     if (!(envpath = getenv("PATH"))) {
354         rs_trace("PATH seems not to be defined");
355         return 0;
356     }
357 
358     rs_trace("original PATH %s", envpath);
359     rs_trace("looking for \"%s\"", compiler_name);
360 
361     /* Allocate a buffer that will let us append "/cc" onto any PATH
362      * element, even if there is only one item in the PATH. */
363     if (!(buf = malloc(strlen(envpath)+1+strlen(compiler_name)+1))) {
364         rs_log_error("failed to allocate buffer for PATH munging");
365         return EXIT_OUT_OF_MEMORY;
366     }
367 
368     for (n = p = envpath, newpath = NULL; *n; p = n) {
369         n = strchr(p, ':');
370         if (n)
371             len = n++ - p;
372         else {
373             len = strlen(p);
374             n = p + len;
375         }
376         strncpy(buf, p, len);
377 
378         sprintf(buf + len, "/%s", compiler_name);
379         if (lstat(buf, &sb) == -1)
380             continue;           /* ENOENT, EACCESS, etc */
381         if (!S_ISLNK(sb.st_mode))
382             break;
383         if ((len = readlink(buf, linkbuf, sizeof linkbuf)) <= 0)
384             continue;
385         linkbuf[len] = '\0';
386         if (strstr(linkbuf, "distcc")) {
387             /* Set newpath to the part of the PATH past our match. */
388             newpath = n;
389         }
390     }
391 
392     if (newpath) {
393         int ret = dcc_set_path(newpath);
394         if (ret)
395             return ret;
396     } else
397         rs_trace("not modifying PATH");
398 
399     free(buf);
400     return 0;
401 }
402 
403 /* Set the PATH environment variable to the indicated value. */
dcc_set_path(const char * newpath)404 int dcc_set_path(const char *newpath)
405 {
406     char *buf;
407 
408     if (asprintf(&buf, "PATH=%s", newpath) <= 0 || !buf) {
409         rs_log_error("failed to allocate buffer for new PATH");
410         return EXIT_OUT_OF_MEMORY;
411     }
412     rs_trace("setting %s", buf);
413     if (putenv(buf) < 0) {
414         rs_log_error("putenv PATH failed");
415         return EXIT_FAILURE;
416     }
417     /* We must leave "buf" allocated. */
418     return 0;
419 }
420 
421 /* Return the supplied path with the current-working directory prefixed (if
422  * needed) and all "dir/.." references removed.  Supply path_len if you want
423  * to use only a substring of the path string, otherwise make it 0. */
dcc_abspath(const char * path,int path_len)424 char *dcc_abspath(const char *path, int path_len)
425 {
426     static char buf[MAXPATHLEN];
427     unsigned len;
428     char *p, *slash;
429 
430     if (*path == '/')
431         len = 0;
432     else {
433         char *ret;
434 #ifdef HAVE_GETCWD
435         ret = getcwd(buf, sizeof buf);
436         if (ret == NULL) {
437           rs_log_crit("getcwd failed: %s", strerror(errno));
438         }
439 #else
440         ret = getwd(buf);
441         if (ret == NULL) {
442           rs_log_crit("getwd failed: %s", strerror(errno));
443         }
444 #endif
445         len = strlen(buf);
446         if (len >= sizeof buf) {
447             rs_log_crit("getwd overflowed in dcc_abspath()");
448         }
449         buf[len++] = '/';
450     }
451     if (path_len <= 0)
452         path_len = strlen(path);
453     if (path_len >= 2 && *path == '.' && path[1] == '/') {
454         path += 2;
455         path_len -= 2;
456     }
457     if (len + (unsigned)path_len >= sizeof buf) {
458         rs_log_error("path overflowed in dcc_abspath()");
459         exit(EXIT_OUT_OF_MEMORY);
460     }
461     strncpy(buf + len, path, path_len);
462     buf[len + path_len] = '\0';
463     for (p = buf+len-(len > 0); (p = strstr(p, "/../")) != NULL; p = slash) {
464         *p = '\0';
465         if (!(slash = strrchr(buf, '/')))
466             slash = p;
467         strcpy(slash, p+3);
468     }
469     return buf;
470 }
471 
472 /* Return -1 if a < b, 0 if a == b, and 1 if a > b */
dcc_timecmp(struct timeval a,struct timeval b)473 int dcc_timecmp(struct timeval a, struct timeval b) {
474     if (a.tv_sec < b.tv_sec) {
475         return -1;
476     } else if (a.tv_sec > b.tv_sec) {
477         return 1;
478     } else if (a.tv_usec < b.tv_usec) {
479         /* at this point their tv_sec must be the same */
480         return -1;
481     } else if (a.tv_usec > b.tv_usec) {
482         return 1;
483     } else {
484         /* they must be equal */
485         return 0;
486     }
487 }
488 
489 
490 /* Return the current number of running processes. */
dcc_getcurrentload(void)491 int dcc_getcurrentload(void) {
492 #if defined(linux)
493   double stats[3];
494   int running;
495   int total;
496   int last_pid;
497   int retval;
498 
499   FILE *f = fopen("/proc/loadavg", "r");
500   if (NULL == f)
501       return -1;
502 
503   retval = fscanf(f, "%lf %lf %lf %d/%d %d", &stats[0], &stats[1], &stats[2],
504                   &running, &total, &last_pid);
505   fclose(f);
506 
507   if (6 != retval)
508       return -1;
509 
510   return running;
511 #else
512   return -1;
513 #endif
514 }
515 
516 /**
517  *  Wrapper for getloadavg() that tries to return all 3 samples, and reports
518  *  -1 for those samples that are not available.
519  *
520  *  Averages are over the last 1, 5, and 15 minutes, respectively.
521  **/
dcc_getloadavg(double loadavg[3])522 void dcc_getloadavg(double loadavg[3]) {
523   int num;
524   int i;
525 
526 #if defined(HAVE_GETLOADAVG)
527   num = getloadavg(loadavg, 3);
528 #else
529   num = 0;
530 #endif
531 
532   /* If getloadavg() didn't return 3 we want to fill
533    * in the invalid elements with -1 */
534   if (num < 0)
535       num = 0;
536 
537   for (i=num; i < 3; ++i)
538       loadavg[i] = -1;
539 }
540 
541 
542 /**
543  * Duplicate the part of the string @p psrc up to a character in @p sep
544  * (or end of string), storing the result in @p pdst.  @p psrc is updated to
545  * point to the terminator.  (If the terminator is not found it will
546  * therefore point to \0.
547  *
548  * If there is no more string, then @p pdst is instead set to NULL, no
549  * memory is allocated, and @p psrc is not advanced.
550  **/
dcc_dup_part(const char ** psrc,char ** pdst,const char * sep)551 int dcc_dup_part(const char **psrc, char **pdst, const char *sep)
552 {
553     size_t len;
554 
555     len = strcspn(*psrc, sep);
556     if (len == 0) {
557         *pdst = NULL;
558     } else {
559         if (!(*pdst = malloc(len + 1))) {
560             rs_log_error("failed to allocate string duplicate: %d", (int) len);
561             return EXIT_OUT_OF_MEMORY;
562         }
563         strncpy(*pdst, *psrc, len);
564         (*pdst)[len] = '\0';
565         (*psrc) += len;
566     }
567 
568     return 0;
569 }
570 
571 
572 
dcc_remove_if_exists(const char * fname)573 int dcc_remove_if_exists(const char *fname)
574 {
575     if (unlink(fname) && errno != ENOENT) {
576         rs_log_warning("failed to unlink %s: %s", fname,
577                        strerror(errno));
578         return EXIT_IO_ERROR;
579     }
580     return 0;
581 }
582 
dcc_which(const char * command,char ** out)583 int dcc_which(const char *command, char **out)
584 {
585     char *loc = NULL, *_loc, *path, *t;
586     int ret;
587 
588     path = getenv("PATH");
589     if (!path)
590         return -ENOENT;
591     do {
592         if (strstr(path, "distcc"))
593             continue;
594         /* emulate strchrnul() */
595         t = strchr(path, ':');
596         if (!t)
597             t = path + strlen(path);
598         _loc = realloc(loc, t - path + 1 + strlen(command) + 1);
599         if (!_loc) {
600             free(loc);
601             return -ENOMEM;
602         }
603         loc = _loc;
604         strncpy(loc, path, t - path);
605         loc[t - path] = '\0';
606         strcat(loc, "/");
607         strcat(loc, command);
608         ret = access(loc, X_OK);
609         if (ret < 0)
610             continue;
611         *out = loc;
612         return 0;
613     } while ((path = strchr(path, ':') + 1));
614     return -ENOENT;
615 }
616 
617 /* Returns the number of processes in state D, the max non-cc/c++ RSS in kb and
618  * the max RSS program's name */
dcc_get_proc_stats(int * num_D,int * max_RSS,char ** max_RSS_name)619 void dcc_get_proc_stats(int *num_D, int *max_RSS, char **max_RSS_name) {
620 #if defined(linux)
621     DIR *proc = opendir("/proc");
622     struct dirent *procsubdir;
623     static int pagesize = -1;
624     static char RSS_name[1024];
625     char statfile[1024];
626     FILE *f;
627     char name[1024];
628     char state;
629     int pid;
630     int rss_size;
631     int l;
632     char *c;
633     int isCC;
634 
635     /* If this doesn't cut it for you, see how CVS does it:
636      * http://savannah.nongnu.org/cgi-bin/viewcvs/cvs/ccvs/lib/getpagesize.h */
637     if (pagesize == -1) {
638 #if HAVE_GETPAGESIZE
639         pagesize = getpagesize();
640 #else
641         pagesize = 8192;
642 #endif
643     }
644 
645     *num_D = 0;
646     *max_RSS = 0;
647     *max_RSS_name = RSS_name;
648     RSS_name[0] = 0;
649 
650     while ((procsubdir = readdir(proc)) != NULL) {
651         if (sscanf(procsubdir->d_name, "%d", &pid) != 1)
652             continue;
653 
654         strcpy(statfile, "/proc/");
655         strcat(statfile, procsubdir->d_name);
656         strcat(statfile, "/stat");
657 
658         f = fopen(statfile, "r");
659         if (f == NULL)
660             continue;
661 
662         if (fscanf(f, "%*d %s %c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d",
663                         name, &state, &rss_size) != 3) {
664             fclose(f);
665             continue;
666         }
667 
668         rss_size = (rss_size * pagesize) / 1024; /* get rss_size in KB */
669 
670         if (state == 'D') {
671             (*num_D)++;
672         }
673 
674         l = strlen(RSS_name);
675         c = RSS_name;
676 
677         /* check for .*{++,cc} */
678         isCC = (l >= 2) && ((c[l-1] == 'c' && c[l-2] == 'c')
679                                 || (c[l-1] == '+' && c[l-2] == '+'));
680         if ((rss_size > *max_RSS) && !isCC) {
681             *max_RSS = rss_size;
682             strncpy(RSS_name, name, 1024);
683         }
684 
685         fclose(f);
686     }
687 
688     closedir(proc);
689 #else
690     static char RSS_name[] = "none";
691     *num_D = -1;
692     *max_RSS = -1;
693     *max_RSS_name = RSS_name;
694 #endif
695 }
696 
697 
698 /* Returns the number of sector read/writes since boot */
dcc_get_disk_io_stats(int * n_reads,int * n_writes)699 void dcc_get_disk_io_stats(int *n_reads, int *n_writes) {
700 #if defined(linux)
701     int retval;
702     int kernel26 = 1;
703     FILE *f;
704     int reads, writes, minor;
705     char dev[100];
706     char tmp[1024];
707 
708     *n_reads = 0;
709     *n_writes = 0;
710 
711     f = fopen("/proc/diskstats", "r");
712     if (f == NULL) {
713         if (errno != ENOENT)
714             return;
715 
716         /* /proc/diskstats does not exist. probably a 2.4 kernel, so try reading
717          * /proc/partitions */
718         f = fopen("/proc/partitions", "r");
719         if (f == NULL)
720             return;
721         kernel26 = 0;
722     }
723 
724     if (!kernel26) /* blast away 2 header lines in /proc/partitions */ {
725         retval = fscanf(f,
726             "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s");
727         if (retval == EOF) {
728             fclose(f);
729             return;
730         }
731     }
732 
733     while (1) {
734         if (kernel26)
735             retval = fscanf(f, " %*d %d %s", &minor, dev);
736         else
737             retval = fscanf(f, " %*d %d %*d %s", &minor, dev);
738 
739         if (retval == EOF || retval != 2)
740             break;
741 
742         if (minor % 64 == 0
743                 && ((dev[0] == 'h' && dev[1] == 'd' && dev[2] == 'a')
744                     || (dev[0] == 's' && dev[1] == 'd' && dev[2] == 'a'))) {
745             /* disk stats */
746             retval = fscanf(f, " %*d %*d %d %*d %*d %*d %d %*d %*d %*d %*d",
747                             &reads, &writes);
748             if (retval == EOF || retval != 2)
749                 break;
750 
751             /* only add stats for disks, so we don't double count */
752             *n_reads += reads;
753             *n_writes += writes;
754         } else {
755 #if 0
756             /* individual partition stats */
757             retval = fscanf(f, " %*d %d %*d %d", &reads, &writes);
758             if (retval == EOF || retval != 2)
759                 break;
760 #endif
761             /* assume the lines aren't longer that 1024 characters */
762             if (fgets(tmp, 1024, f) == NULL)
763               break;
764         }
765     }
766 
767     fclose(f);
768 #else
769     *n_reads = 0;
770     *n_writes = 0;
771 #endif
772 }
773 
774 
775 #ifndef HAVE_STRLCPY
776 /* like strncpy but does not 0 fill the buffer and always null
777    terminates. bufsize is the size of the destination buffer */
strlcpy(char * d,const char * s,size_t bufsize)778  size_t strlcpy(char *d, const char *s, size_t bufsize)
779 {
780     size_t len = strlen(s);
781     size_t ret = len;
782     if (bufsize <= 0) return 0;
783     if (len >= bufsize) len = bufsize-1;
784     memcpy(d, s, len);
785     d[len] = 0;
786     return ret;
787 }
788 #endif
789 
790 #ifndef HAVE_STRSEP
strsep(char ** str,const char * delims)791 static char* strsep(char** str, const char* delims)
792 {
793     char* token;
794 
795     if (*str == NULL) {
796         return NULL;
797     }
798 
799     token = *str;
800     while (**str != '\0') {
801         if (strchr(delims, **str) != NULL) {
802             **str = '\0';
803             (*str)++;
804             return token;
805         }
806         (*str)++;
807     }
808     *str = NULL;
809     return token;
810 }
811 #endif
812 
813 /* Given a string @p input, this function fills a
814    a newly-allocated array of strings with copies of
815    the input's whitespace-separated parts.
816    Returns 0 on success, 1 on error.
817  */
dcc_tokenize_string(const char * input,char *** argv_ptr)818 int dcc_tokenize_string(const char *input, char ***argv_ptr)
819 {
820     size_t n_spaces = 0;
821     char *for_count;
822     char **ap;
823     char *input_copy;
824 
825     /* First of all, make a copy of the input string;
826      * this way, we can destroy the copy.
827      */
828     input_copy = strdup(input);
829     if (input_copy == NULL)
830         return 1;
831 
832     /* Count the spaces in the string. */
833     for (for_count = input_copy; *for_count; for_count++)
834         if (isspace((uint8_t)*for_count))
835             n_spaces++;
836 
837     /* The maximum number of space-delimited strings we
838      * can have is n_spaces + 1, and we need to add another 1 for
839      * the null-termination.
840      */
841     *argv_ptr = malloc(sizeof(char*) * (n_spaces + 1 + 1));
842     if (*argv_ptr == NULL) {
843         free(input_copy);
844         return 1;
845     }
846 
847     ap = *argv_ptr;
848     while((*ap = strsep(&input_copy, " \t\n")) != NULL) {
849 
850         /* If the field is empty, do nothing */
851       if (**ap == '\0')
852           continue;
853 
854       *ap = strdup(*ap);
855       if (*ap == NULL) {
856           char **p;
857           for (p = *argv_ptr; *p; p++) {
858             free(*p);
859           }
860           free(*argv_ptr);
861           free(input_copy);
862           return 1;
863       }
864 
865       ap++;
866     }
867     free(input_copy);
868     return 0;
869 }
870 
871 #ifndef HAVE_GETLINE
getline(char ** lineptr,size_t * n,FILE * stream)872 ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
873     static const int buffer_size_increment = 100;
874     char *buffer;
875     size_t size;
876     size_t bytes_read;
877     int c;
878     char *new_buffer;
879 
880     if (lineptr == NULL || stream == NULL || n == NULL ||
881             (*lineptr == NULL && *n != 0)) {
882         /* Invalid parameters. */
883         return -1;
884     }
885 
886     buffer = *lineptr;
887     size = *n;
888 
889     bytes_read = 0;
890     do {
891         /* Ensure that we have space for next character or '\0'. */
892         if (bytes_read + 1 > size) {
893             size += buffer_size_increment;
894             new_buffer = realloc(buffer, size);
895             if (new_buffer == NULL) {
896                 /* Out of memory. */
897                 *lineptr = buffer;
898                 *n = size - buffer_size_increment;
899                 return -1;
900             }
901             buffer = new_buffer;
902         }
903         if ((c = fgetc(stream)) == EOF)
904             break;
905         buffer[bytes_read++] = c;
906     } while (c != '\n');
907     buffer[bytes_read] = '\0';
908 
909     *lineptr = buffer;
910     *n = size;
911 
912     /* We return -1 on EOF for compatibility with GNU getline(). */
913     return bytes_read == 0 ? -1 : (ssize_t) bytes_read;
914 }
915 #endif
916 
917 /* from old systemd
918 
919    Copyright 2010 Lennart Poettering
920 
921    Permission is hereby granted, free of charge, to any person
922    obtaining a copy of this software and associated documentation files
923    (the "Software"), to deal in the Software without restriction,
924    including without limitation the rights to use, copy, modify, merge,
925    publish, distribute, sublicense, and/or sell copies of the Software,
926    and to permit persons to whom the Software is furnished to do so,
927    subject to the following conditions:
928 
929    The above copyright notice and this permission notice shall be
930    included in all copies or substantial portions of the Software.
931 
932    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
933    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
934    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
935    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
936    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
937    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
938    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
939    SOFTWARE.
940  */
sd_is_socket_internal(int fd,int type,int listening)941 static int sd_is_socket_internal(int fd, int type, int listening) {
942         struct stat st_fd;
943 
944         if (fd < 0 || type < 0)
945                 return -EINVAL;
946 
947         if (fstat(fd, &st_fd) < 0)
948                 return -errno;
949 
950         if (!S_ISSOCK(st_fd.st_mode))
951                 return 0;
952 
953         if (type != 0) {
954                 int other_type = 0;
955                 socklen_t l = sizeof(other_type);
956 
957                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
958                         return -errno;
959 
960                 if (l != sizeof(other_type))
961                         return -EINVAL;
962 
963                 if (other_type != type)
964                         return 0;
965         }
966 
967         if (listening >= 0) {
968                 int accepting = 0;
969                 socklen_t l = sizeof(accepting);
970 
971                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
972                         return -errno;
973 
974                 if (l != sizeof(accepting))
975                         return -EINVAL;
976 
977                 if (!accepting != !listening)
978                         return 0;
979         }
980 
981         return 1;
982 }
983 
984 union sockaddr_union {
985         struct sockaddr sa;
986         struct sockaddr_in in4;
987         struct sockaddr_in6 in6;
988         struct sockaddr_un un;
989         struct sockaddr_storage storage;
990 };
991 
not_sd_is_socket(int fd,int family,int type,int listening)992 int not_sd_is_socket(int fd, int family, int type, int listening) {
993         int r;
994 
995         if (family < 0)
996                 return -EINVAL;
997 
998         r = sd_is_socket_internal(fd, type, listening);
999         if (r <= 0)
1000                 return r;
1001 
1002         if (family > 0) {
1003                 union sockaddr_union sockaddr = {};
1004                 socklen_t l = sizeof(sockaddr);
1005 
1006                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
1007                         return -errno;
1008 
1009                 if ((size_t)l < sizeof(sa_family_t))
1010                         return -EINVAL;
1011 
1012                 return sockaddr.sa.sa_family == family;
1013         }
1014 
1015         return 1;
1016 }
1017