1 /*
2 ** Copyright (C) 2001-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9 /*
10 ** sku-filesys.c
11 **
12 ** A collection of utility routines dealing with the file system.
13 **
14 ** Suresh L Konda
15 */
16
17
18 #include <silk/silk.h>
19
20 RCSIDENT("$SiLK: sku-filesys.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
21
22 #include <silk/utils.h>
23
24
25 /* DEFINES AND TYPEDEFS */
26
27 /* Maximum size to attempt to mmap at a time */
28 #define DEFAULT_MAX_MMAPSIZE ((size_t)1 << 26)
29
30
31 /* FUNCTION DEFINITIONS */
32
33 char *
skBasename_r(char * dest,const char * src,size_t dest_size)34 skBasename_r(
35 char *dest,
36 const char *src,
37 size_t dest_size)
38 {
39 const char *startp;
40 const char *endp;
41 size_t src_len;
42
43 /* check input: need space for {'.', '\0'} minimally */
44 if (!dest || dest_size < 2) {
45 return NULL;
46 }
47
48 /* degenerate cases */
49 if (!src || (0 == (src_len = strlen(src)))) {
50 return strncpy(dest, ".", 2);
51 }
52
53 startp = strrchr(src, '/');
54 if (!startp) {
55 /* no slash; return what we were given */
56 startp = src;
57 endp = src + src_len;
58 } else if ('\0' != *(startp+1)) {
59 /* typical case: "/bin/cat" */
60 ++startp;
61 endp = src + src_len;
62 } else {
63 /* we could have "/", "///", "usr/", or "/usr/lib/" */
64 while (startp > src && *startp == '/') {
65 /* remove trailing '/' */
66 --startp;
67 }
68 endp = startp + 1;
69 /* go backward until find '/'; startp is char after the '/' */
70 while (startp > src) {
71 --startp;
72 if (*startp == '/') {
73 ++startp;
74 break;
75 }
76 }
77 }
78
79 /* need to grab everything between startp and endp */
80 src_len = endp - startp;
81 if (src_len > dest_size-1) {
82 return NULL;
83 }
84 strncpy(dest, startp, src_len);
85 dest[src_len] = '\0';
86
87 return dest;
88 }
89
90
91 char *
skDirname_r(char * dest,const char * src,size_t dest_size)92 skDirname_r(
93 char *dest,
94 const char *src,
95 size_t dest_size)
96 {
97 const char *endp;
98 size_t src_len;
99
100 /* check input: need space for {'.', '\0'} minimally */
101 if (!dest || dest_size < 2) {
102 return NULL;
103 }
104
105 /* degenerate cases */
106 if (!src || !(endp = strrchr(src, '/'))) {
107 return strncpy(dest, ".", 2);
108 }
109
110 if ('\0' == *(endp+1)) {
111 /* we could have "/", "///", "usr/", or "/usr/lib/" */
112 while (endp > src && *endp == '/') {
113 /* remove trailing '/' */
114 --endp;
115 }
116 while (endp > src && *endp != '/') {
117 /* skip basename */
118 --endp;
119 }
120 if (*endp != '/') {
121 /* we're at start of string */
122 return strncpy(dest, ".", 2);
123 }
124 }
125
126 /* handle duplicate '/' chars */
127 while (endp > src && *endp == '/') {
128 --endp;
129 }
130
131 src_len = endp - src + 1;
132 if (src_len > dest_size-1) {
133 return NULL;
134 }
135
136 strncpy(dest, src, src_len);
137 dest[src_len] = '\0';
138
139 return dest;
140 }
141
142
143 char *
skBasename(const char * src)144 skBasename(
145 const char *src)
146 {
147 static char dest[PATH_MAX]; /* return pointer */
148
149 return skBasename_r(dest, src, sizeof(dest));
150 }
151
152
153 char *
skDirname(const char * src)154 skDirname(
155 const char *src)
156 {
157 static char dest[PATH_MAX]; /* return pointer */
158
159 return skDirname_r(dest, src, sizeof(dest));
160 }
161
162
163 int
isFIFO(const char * name)164 isFIFO(
165 const char *name)
166 {
167 struct stat stBuf;
168 if (stat(name, &stBuf) == -1) {
169 return 0;
170 }
171 return (S_ISFIFO(stBuf.st_mode));
172 }
173
174
175 int
skDirExists(const char * dName)176 skDirExists(
177 const char *dName)
178 {
179 struct stat stBuf;
180 if (stat(dName, &stBuf) == -1) {
181 return 0; /* does not exist */
182 }
183 /* return a 1 only if this is a directory */
184 return S_ISDIR(stBuf.st_mode);
185 }
186
187
188 int
skFileExists(const char * fName)189 skFileExists(
190 const char *fName)
191 {
192 struct stat stBuf;
193 if (stat(fName, &stBuf) == -1) {
194 return 0; /* does not exist */
195 }
196 /* return a 1 only if this is a regular file */
197 return (S_ISREG(stBuf.st_mode) || S_ISFIFO(stBuf.st_mode));
198 }
199
200
201 off_t
skFileSize(const char * fName)202 skFileSize(
203 const char *fName)
204 {
205 struct stat stBuf;
206 if (stat(fName, &stBuf) == -1) {
207 return 0; /* does not exist */
208 }
209 /* it exists. return the size */
210 return stBuf.st_size;
211 }
212
213
214 /*
215 * Lock or unlock the file 'fd'. See header for details.
216 */
217 int
skFileSetLock(int fd,short type,int cmd)218 skFileSetLock(
219 int fd,
220 short type,
221 int cmd)
222 {
223 struct flock lock;
224
225 lock.l_type = type;
226 lock.l_start = 0; /* at SOF */
227 lock.l_whence = SEEK_SET; /* SOF */
228 lock.l_len = 0; /* EOF */
229
230 if (fcntl(fd, cmd, &lock) != -1) {
231 /* success */
232 return 0;
233 }
234
235 return -1;
236 }
237
238
239 /* Find the file 'base_name' and return its full path in 'buf'. See
240 * the header for details. */
241 char *
skFindFile(const char * base_name,char * buf,size_t bufsize,int verbose)242 skFindFile(
243 const char *base_name,
244 char *buf,
245 size_t bufsize,
246 int verbose)
247 {
248 const char *app_name = skAppName();
249 char *silkpath = getenv(ENV_SILK_PATH);
250 size_t len = 0;
251 int rv;
252
253 /* check inputs */
254 if (!base_name || !buf || bufsize <= 1) {
255 return NULL;
256 }
257
258 /* if base_name begins with a slash, use it */
259 if (base_name[0] == '/') {
260 strncpy(buf, base_name, bufsize);
261 buf[bufsize - 1] = '\0';
262 return buf;
263 }
264
265 /* Check in $SILK_PATH/share/silk and $SILK_PATH/share */
266 if (silkpath) {
267 rv = snprintf(buf, bufsize, "%s/share/silk/%s", silkpath, base_name);
268 if ((size_t)rv < bufsize && skFileExists(buf)) {
269 return buf;
270 }
271 rv = snprintf(buf, bufsize, "%s/share/%s", silkpath, base_name);
272 if ((size_t)rv < bufsize && skFileExists(buf)) {
273 return buf;
274 }
275 }
276
277 /* Look in binarypath/../share. First, get the parent directory of
278 * the executable and store in 'buf'. */
279 if (app_name == (char *)NULL) {
280 goto ERROR;
281 }
282 if (NULL == skAppDirParentDir(buf, bufsize)) {
283 buf[0] = '\0';
284 goto ERROR;
285 }
286 len = strlen(buf);
287
288 /* Now append "/share/silk/<file>" to it */
289 rv = snprintf((buf+len), (bufsize - len - 1), "/share/silk/%s", base_name);
290 if ((size_t)rv < bufsize && skFileExists(buf)) {
291 return buf;
292 }
293
294 /* Try appending "/share/<file>" to it */
295 rv = snprintf((buf+len), (bufsize - len - 1), "/share/%s", base_name);
296 if ((size_t)rv < bufsize && skFileExists(buf)) {
297 return buf;
298 }
299
300 ERROR:
301 if (verbose) {
302 #define ERR_MSG \
303 "Cannot find file '%s' in $" ENV_SILK_PATH "/share/silk/,\n" \
304 "\tin $" ENV_SILK_PATH "/share/, in $" ENV_SILK_PATH "/, "
305
306 if (!app_name) {
307 skAppPrintErr((ERR_MSG "and application not registered"),
308 base_name);
309 }
310 else if ('\0' == buf[0]) {
311 skAppPrintErr((ERR_MSG "and cannot obtain full path to\n"
312 "\tthe application '%s'"),
313 base_name, app_name);
314 }
315 else {
316 buf[len] = '\0';
317 skAppPrintErr((ERR_MSG "nor in the share/silk/ and share/\n"
318 "\tsubdirectories under %s/"),
319 base_name, buf);
320 }
321 #undef ERR_MSG
322 }
323
324 return NULL;
325 }
326
327
328 char *
skFindPluginPath(const char * dlPath,char * path,size_t path_size,const char * verbose_prefix)329 skFindPluginPath(
330 const char *dlPath,
331 char *path,
332 size_t path_size,
333 const char *verbose_prefix)
334 {
335 #ifndef SILK_SUBDIR_PLUGINS
336 return NULL;
337 #else
338 const char *subdir[] = SILK_SUBDIR_PLUGINS;
339 char *silkPath;
340 size_t len;
341 int i;
342 int8_t checkSilkPath = 1;
343 int8_t checkExec = 1;
344
345 /* put path into known state */
346 path[0] = '\0';
347
348 if (strchr(dlPath, '/')) {
349 return NULL;
350 }
351
352 /* if dlPath does not contain a slash, first look for the plugin in
353 * the SILK_SUBDIR_PLUGINS subdirectory of the environment variable
354 * named by ENV_SILK_PATH. If the plugin does not exist there, pass
355 * the dlPath as given to dlopen() which will use LD_LIBRARY_PATH or
356 * equivalent.
357 */
358 while (checkSilkPath || checkExec) {
359 if (checkSilkPath) {
360 checkSilkPath = 0;
361 if (NULL == (silkPath = getenv(ENV_SILK_PATH))) {
362 continue;
363 }
364 strncpy(path, silkPath, path_size);
365 } else if (checkExec) {
366 checkExec = 0;
367 if (NULL == skAppDirParentDir(path, path_size)) {
368 /* cannot find executeable path */
369 continue;
370 }
371 }
372 path[path_size-1] = '\0';
373 len = strlen(path);
374 for (i = 0; subdir[i]; ++i) {
375 snprintf(path + len, path_size - len - 1, "/%s/%s",
376 subdir[i], dlPath);
377 path[path_size-1] = '\0';
378 if (verbose_prefix) {
379 skAppPrintErr("%s%s", verbose_prefix, path);
380 }
381 if (skFileExists(path)) {
382 return path;
383 }
384 }
385 }
386
387 /* file does not exist. Fall back to LD_LIBRARY_PATH */
388 path[0] = '\0';
389 return NULL;
390 #endif /* SILK_SUBDIR_PLUGINS */
391 }
392
393
394 /* open a file, stream, or process */
395 int
skFileptrOpen(sk_fileptr_t * file,skstream_mode_t io_mode)396 skFileptrOpen(
397 sk_fileptr_t *file,
398 skstream_mode_t io_mode)
399 {
400 #ifdef SILK_CLOBBER_ENVAR
401 const char *clobber_env;
402 #endif
403 char gzip_cmd[16 + PATH_MAX];
404 const char *gzip_mode;
405 const char *fopen_mode;
406 struct stat stbuf;
407 size_t len;
408 int flags;
409 int mode;
410 int fd;
411 int rv;
412
413 assert(file);
414 if (NULL == file->of_name) {
415 return SK_FILEPTR_ERR_INVALID;
416 }
417 switch (io_mode) {
418 case SK_IO_READ:
419 case SK_IO_WRITE:
420 case SK_IO_APPEND:
421 break;
422 default:
423 return SK_FILEPTR_ERR_INVALID; /* NOTREACHED */
424 }
425
426 /* handle stdio */
427 if (0 == strcmp(file->of_name, "-")) {
428 file->of_type = SK_FILEPTR_IS_STDIO;
429 switch (io_mode) {
430 case SK_IO_READ:
431 file->of_fp = stdin;
432 return 0;
433
434 case SK_IO_WRITE:
435 case SK_IO_APPEND:
436 file->of_fp = stdout;
437 return 0;
438 }
439 }
440 if (0 == strcmp(file->of_name, "stdin")) {
441 if (SK_IO_READ == io_mode) {
442 file->of_fp = stdin;
443 file->of_type = SK_FILEPTR_IS_STDIO;
444 return 0;
445 }
446 return SK_FILEPTR_ERR_WRITE_STDIN;
447 }
448 if (0 == strcmp(file->of_name, "stdout")) {
449 if (SK_IO_READ == io_mode) {
450 return SK_FILEPTR_ERR_READ_STDOUT;
451 }
452 file->of_fp = stdout;
453 file->of_type = SK_FILEPTR_IS_STDIO;
454 return 0;
455 }
456 if (0 == strcmp(file->of_name, "stderr")) {
457 if (SK_IO_READ == io_mode) {
458 return SK_FILEPTR_ERR_READ_STDERR;
459 }
460 file->of_fp = stderr;
461 file->of_type = SK_FILEPTR_IS_STDIO;
462 return 0;
463 }
464
465 /* check whether file->of_name indicates file is compressed */
466 len = strlen(file->of_name);
467 if (len > 3 && 0 == strcmp(&file->of_name[len-3], ".gz")) {
468 switch (io_mode) {
469 case SK_IO_READ:
470 gzip_mode = "-d";
471 fopen_mode = "r";
472 break;
473 case SK_IO_WRITE:
474 if (skFileExists(file->of_name)) {
475 #ifdef SILK_CLOBBER_ENVAR
476 if ((clobber_env = getenv(SILK_CLOBBER_ENVAR)) != NULL
477 && *clobber_env && *clobber_env != '0')
478 {
479 /* overwrite existing files */
480 } else
481 #endif /* SILK_CLOBBER_ENVAR */
482 {
483 errno = EEXIST;
484 return SK_FILEPTR_ERR_ERRNO;
485 }
486 }
487 gzip_mode = ">";
488 fopen_mode = "w";
489 break;
490 case SK_IO_APPEND:
491 if (skFileExists(file->of_name)) {
492 gzip_mode = ">>";
493 } else {
494 gzip_mode = ">";
495 }
496 fopen_mode = "w";
497 break;
498 default:
499 skAbort();
500 }
501
502 rv = snprintf(gzip_cmd, sizeof(gzip_cmd), "gzip -c %s '%s'",
503 gzip_mode, file->of_name);
504 if (sizeof(gzip_cmd) < (size_t)rv) {
505 return SK_FILEPTR_ERR_TOO_LONG;
506 }
507 errno = 0;
508 file->of_fp = popen(gzip_cmd, fopen_mode);
509 if (NULL == file->of_fp) {
510 if (errno) {
511 return SK_FILEPTR_ERR_ERRNO;
512 }
513 return SK_FILEPTR_ERR_POPEN;
514 }
515 file->of_type = SK_FILEPTR_IS_PROCESS;
516 return 0;
517 }
518
519 /* handle a standard fopen() for read */
520 if (SK_IO_READ == io_mode) {
521 file->of_fp = fopen(file->of_name, "r");
522 if (NULL == file->of_fp) {
523 return SK_FILEPTR_ERR_ERRNO;
524 }
525 file->of_type = SK_FILEPTR_IS_FILE;
526 return 0;
527 }
528
529 /* handle a standard fopen() for write or append. use open()
530 * first, since it gives us better control. */
531
532 fopen_mode = "w";
533 /* standard mode of 0666 */
534 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
535 /* assume creating previously non-existent file */
536 flags = O_WRONLY | O_CREAT | O_EXCL;
537
538 /* try to open as a brand new file */
539 fd = open(file->of_name, flags, mode);
540 if (fd == -1) {
541 rv = errno;
542 if ((rv == EEXIST)
543 && (0 == stat(file->of_name, &stbuf)))
544 {
545 /* file exists. Try again with different flags when the
546 * mode is append, the file is a FIFO, the file is a
547 * character device ("/dev/null"), or the SILK_CLOBBER
548 * envar is set. */
549 if (io_mode == SK_IO_APPEND) {
550 flags = O_WRONLY | O_APPEND;
551 fopen_mode = "a";
552 } else if (S_ISFIFO(stbuf.st_mode)) {
553 flags = O_WRONLY;
554 } else if (S_ISCHR(stbuf.st_mode)) {
555 flags = O_WRONLY | O_NOCTTY;
556 #ifdef SILK_CLOBBER_ENVAR
557 } else if ((clobber_env = getenv(SILK_CLOBBER_ENVAR)) != NULL
558 && *clobber_env && *clobber_env != '0')
559 {
560 /* overwrite existing file */
561 flags = O_WRONLY | O_TRUNC;
562 #endif /* SILK_CLOBBER_ENVAR */
563 } else {
564 errno = rv;
565 return SK_FILEPTR_ERR_ERRNO;
566 }
567
568 /* try again with the new flags */
569 fd = open(file->of_name, flags, mode);
570 }
571 /* if we (still) have an error, return */
572 if (fd == -1) {
573 return SK_FILEPTR_ERR_ERRNO;
574 }
575 }
576 file->of_fp = fdopen(fd, fopen_mode);
577 if (NULL == file->of_fp) {
578 rv = errno;
579 close(fd);
580 errno = rv;
581 return SK_FILEPTR_ERR_ERRNO;
582 }
583
584 file->of_type = SK_FILEPTR_IS_FILE;
585 return 0;
586 }
587
588
589 int
skFileptrClose(sk_fileptr_t * file,sk_msg_fn_t err_fn)590 skFileptrClose(
591 sk_fileptr_t *file,
592 sk_msg_fn_t err_fn)
593 {
594 int rv = 0;
595
596 assert(file);
597 if (NULL == file->of_fp) {
598 return 0;
599 }
600
601 switch (file->of_type) {
602 case SK_FILEPTR_IS_STDIO:
603 /* ignore if reading stdin */
604 if (file->of_fp != stdin) {
605 rv = fflush(file->of_fp);
606 if (EOF == rv && err_fn) {
607 err_fn("Error flushing %s: %s",
608 (file->of_name ? file->of_name : "stream"),
609 strerror(errno));
610 }
611 }
612 break;
613
614 case SK_FILEPTR_IS_FILE:
615 rv = fclose(file->of_fp);
616 if (EOF == rv && err_fn) {
617 if (file->of_name) {
618 err_fn("Error closing file '%s': %s",
619 file->of_name, strerror(errno));
620 } else {
621 err_fn("Error closing file: %s",
622 strerror(errno));
623 }
624 }
625 break;
626
627 case SK_FILEPTR_IS_PROCESS:
628 rv = pclose(file->of_fp);
629 if (err_fn) {
630 if (-1 == rv) {
631 if (file->of_name) {
632 err_fn("Error closing output process for '%s'",
633 file->of_name);
634 } else {
635 err_fn("Error closing output process");
636 }
637 } else if (127 == rv) {
638 if (file->of_name) {
639 err_fn("Error starting subprocess for '%s'",
640 file->of_name);
641 } else {
642 err_fn("Error starting subprocess");
643 }
644 }
645 }
646 break;
647
648 default:
649 skAbortBadCase(file->of_type);
650 }
651
652 return rv;
653 }
654
655
656 const char *
skFileptrStrerror(int errnum)657 skFileptrStrerror(
658 int errnum)
659 {
660 static char buf[128];
661
662 switch ((sk_fileptr_status_t)errnum) {
663 case SK_FILEPTR_OK:
664 return "Success";
665 case SK_FILEPTR_ERR_ERRNO:
666 return strerror(errno);
667 case SK_FILEPTR_ERR_WRITE_STDIN:
668 return "Cannot write to the standard input";
669 case SK_FILEPTR_ERR_READ_STDOUT:
670 return "Cannot read from the standard output";
671 case SK_FILEPTR_ERR_READ_STDERR:
672 return "Cannot read from the standard error";
673 case SK_FILEPTR_ERR_POPEN:
674 return "Failed to open process";
675 case SK_FILEPTR_ERR_TOO_LONG:
676 return "Path name is too long";
677 case SK_FILEPTR_ERR_INVALID:
678 return "Invalid input to function";
679 case SK_FILEPTR_PAGER_IGNORED:
680 return "Not paging the output";
681 }
682
683 snprintf(buf, sizeof(buf), "Unrecognized skFileptrOpen() return value %d\n",
684 errnum);
685 return buf;
686 }
687
688
689 /* open file 'FName' for read (mode==0) or write (mode==1). See header. */
690 int
skOpenFile(const char * FName,int mode,FILE ** fp,int * isPipe)691 skOpenFile(
692 const char *FName,
693 int mode,
694 FILE **fp,
695 int *isPipe)
696 {
697 char cmd[1024];
698 const char *cp;
699 int fd;
700 unsigned char magic[2];
701 ssize_t num_read;
702
703 /* after this while() loop, 'cp' will be NULL if 'FName' is NOT
704 * compressed, or non-NULL if 'FName' is compressed. */
705 cp = FName;
706 while (NULL != (cp = strstr(cp, ".gz"))) {
707 if (*(cp + 3) == '\0') {
708 /* file ends with ".gz". Treat it as compressed. (In
709 * truth, this can be fooled by a bad mkstemp-based
710 * filename as below, but we'll worry about that
711 * later.) */
712 break;
713 } else if (*(cp + 3) == '.') {
714 /* Treat a file that contains ".gz." as a potential
715 * compressed file. We do this to handle compressed files
716 * have had mkstemp() extensions added to them. This is
717 * hackish, but it is simple and covers that common case
718 * with few false positives. We then, if possible, check
719 * to see if it really is compressed by looking for the
720 * gzip magic number (31 139 (see RFC1952)) in the first
721 * two bytes. We do not do this, however, if we are
722 * writing to the file or if we are working with a named
723 * pipe. Despite the ability to search for the two-byte
724 * marker in a named pipe stream, we have no way to put
725 * the bytes back for normal processing. Another solution
726 * would be to use gzopen() and gzread()---which again
727 * won't work for writing---and either accept their
728 * overhead when working with uncompressed files, or have
729 * librw do special things with compressed files. There
730 * is really no good solution here. Hopefully soon we
731 * will be using LZO compression in the body of the data
732 * files, but still have an uncompresed SiLK header. */
733 if ((1 == mode) || isFIFO(FName)) {
734 /* We will assume it is compressed if we are writing
735 * to the file, or it is a FIFO, since we can't get
736 * more information (such as a magic number) from the
737 * file. */
738 break;
739 }
740 fd = open(FName, O_RDONLY);
741 if (-1 == fd) {
742 /* We couldn't open the file. Pass it on for normal
743 * processing and error handling. */
744 break;
745 }
746 /* Read the first two bytes of the file. */
747 num_read = read(fd, magic, 2);
748 if ((num_read != 2) || (magic[0] != 31) || (magic[1] != 139)) {
749 /* This cannot be a gzip compressed file, as it does
750 * not contain the gzip magic number. Setting cp to
751 * NULL indicates that the file is not compressed. */
752 cp = NULL;
753 }
754 close(fd);
755 break;
756 } else {
757 cp += 3;
758 }
759 }
760
761 if (NULL == cp) {
762 /* Regular file or named pipe */
763 *isPipe = 0;
764 *fp = fopen(FName, mode ? "w" : "r");
765 } else if (mode == 0 && skFileExists(FName) == 0) {
766 /* Attempting to read from non-existent gzip */
767 *fp = NULL;
768 } else {
769 /* Either writing to gzip or reading from existing gzip */
770 if ((sizeof(cmd) - 16u) < strlen(FName)) {
771 return 1;
772 }
773 *isPipe = 1;
774 snprintf(cmd, sizeof(cmd), "gzip %s '%s'",
775 (mode ? ">" : "-d -c"), FName);
776 *fp = popen(cmd, mode ? "w" : "r");
777 }
778
779 if (*fp == (FILE *)NULL) {
780 if (mode == 0 && skFileExists(FName) == 0) {
781 skAppPrintErr("Cannot open non-existant file '%s'", FName);
782 } else {
783 skAppPrintErr("Unable to open file '%s' for %s",
784 FName, mode ? "writing" : "reading");
785 }
786 return 1; /* error */
787 }
788
789 return 0;
790 }
791
792
793 /*
794 * status = skMakeDir(path);
795 *
796 * Make the complete directory path to 'path', including parent(s)
797 * if required. Return 0 on success. Return 1 on failure and
798 * sets errno.
799 */
800 int
skMakeDir(const char * directory)801 skMakeDir(
802 const char *directory)
803 {
804 int rv = 1; /* return value */
805 int rv_err = 0; /* errno to set */
806 size_t dir_len;
807 char *cp;
808 char *dir_buf = NULL;
809 char **slash_list = NULL;
810 int slash_count = 0;
811 const mode_t dirMode = /* 0775 */
812 S_IRWXU | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH;
813
814 assert(directory);
815
816 /* Try common case first, where only trailing directory is
817 * missing. AIX does not always set errno to EEXIST for an
818 * existing directory, so call skDirExists() as a back-up test. */
819 errno = 0;
820 if (0 == mkdir(directory, dirMode)
821 || errno == EEXIST
822 || skDirExists(directory))
823 {
824 return 0;
825 }
826
827 dir_len = strlen(directory);
828 if (0 == dir_len) {
829 /* ENOENT is what ``mkdir("")'' returns */
830 rv_err = ENOENT;
831 goto END;
832 }
833
834 /* make a copy of the directory name that we can modify, and malloc
835 * an array of char*'s that will point to the slashes ('/') in that
836 * dir_buf. */
837 if (((dir_buf = strdup(directory)) == NULL)
838 || ((slash_list = (char**)malloc(dir_len * sizeof(char*))) == NULL))
839 {
840 rv_err = errno;
841 goto END;
842 }
843
844 /* point cp at the end of the buffer, then work backward until we
845 * find a slash. Convert the slash to a '\0' and see if the parent
846 * dir exists. If it does not, keep shorting the directory
847 * path--stashing the locations of the '/' in slash_list[]--until we
848 * find an existing parent directory. If the parent dir does exist,
849 * change the '\0' back to a '/' and start making child
850 * directories. */
851 cp = &(dir_buf[dir_len]);
852
853 for (;;) {
854 /* search for dir-sep */
855 while (cp > dir_buf && *cp != '/') {
856 --cp;
857 }
858 if (cp == dir_buf) {
859 /* can't search past start of string */
860 break;
861 }
862 /* see if parent dir exists... */
863 *cp = '\0';
864 if (skDirExists(dir_buf)) {
865 /* ...it does, we can start making child directories */
866 *cp = '/';
867 break;
868 }
869 /* ...else it does not. Store this location and continue up the path */
870 slash_list[slash_count] = cp;
871 ++slash_count;
872 }
873
874 /* dir_buf should contain a directory we can create */
875 for (;;) {
876 /* make the child directory */
877 if (0 != mkdir(dir_buf, dirMode)) {
878 /* perhaps another thread or process created the directory? */
879 rv_err = errno;
880 if (rv_err != EEXIST && !skDirExists(dir_buf)) {
881 goto END;
882 }
883 }
884 if (slash_count == 0) {
885 /* we should have created the entire path */
886 assert(0 == strcmp(dir_buf, directory));
887 break;
888 }
889 /* convert this '\0' back to a '/' and make next child dir */
890 --slash_count;
891 *(slash_list[slash_count]) = '/';
892 }
893
894 /* success! */
895 rv = 0;
896
897 END:
898 /* cleanup buffers */
899 if (dir_buf) {
900 free(dir_buf);
901 }
902 if (slash_list) {
903 free(slash_list);
904 }
905 if (rv) {
906 errno = rv_err;
907 }
908 return rv;
909 }
910
911
912 /*
913 * skCopyFile:
914 * Copies the file "source" to "dest". "Dest" may be a file or a
915 * directory.
916 * Input: char * source
917 * char * dest
918 * Output: 0 on success, errno on failure.
919 */
920 int
skCopyFile(const char * srcPath,const char * destPath)921 skCopyFile(
922 const char *srcPath,
923 const char *destPath)
924 {
925 static size_t max_mapsize = DEFAULT_MAX_MMAPSIZE;
926 int fdin = -1, fdout = -1;
927 void *src = NULL, *dst = NULL;
928 const char *dest = NULL;
929 char destBuf[PATH_MAX];
930 char base[PATH_MAX];
931 struct stat st;
932 int saveerrno;
933 int rv;
934 off_t orv;
935 ssize_t wrv;
936 off_t offset;
937 off_t size;
938 size_t mapsize = 0;
939 int pagesize = sysconf(_SC_PAGESIZE);
940
941 max_mapsize -= max_mapsize % pagesize;
942
943 /* Handle dest being a directory */
944 if (skDirExists(destPath)) {
945 skBasename_r(base, srcPath, sizeof(base));
946 rv = snprintf(destBuf, sizeof(destBuf), "%s/%s", destPath, base);
947 if (rv == -1) {
948 return EIO;
949 }
950 if ((unsigned int)rv > (sizeof(destBuf) - 1)) {
951 return ENAMETOOLONG;
952 }
953 dest = destBuf;
954 } else {
955 dest = destPath;
956 }
957
958 /* Open source */
959 fdin = open(srcPath, O_RDONLY);
960 if (fdin == -1) {
961 goto copy_error;
962 }
963
964 /* Get source info */
965 rv = fstat(fdin, &st);
966 if (rv == -1) {
967 goto copy_error;
968 }
969 size = st.st_size;
970
971 /* Open dest */
972 fdout = open(dest, O_RDWR | O_CREAT | O_TRUNC, st.st_mode);
973 if (fdout == -1) {
974 goto copy_error;
975 }
976
977 /* Resize dest to source size (For mmap. See APUE [Stevens].) */
978 orv = lseek(fdout, size - 1, SEEK_SET);
979 if (orv == -1) {
980 goto copy_error;
981 }
982 wrv = write(fdout, "", 1);
983 if (wrv != 1) {
984 goto copy_error;
985 }
986
987 offset = 0;
988 while (size) {
989 mapsize = (size > (off_t)max_mapsize) ? max_mapsize : (size_t)size;
990
991 /* Map source */
992 src = mmap(0, mapsize, PROT_READ, MAP_SHARED, fdin, offset);
993 if (src == MAP_FAILED) {
994 if (errno == ENOMEM) {
995 max_mapsize >>= 1;
996 max_mapsize -= max_mapsize % pagesize;
997 continue;
998 }
999 goto copy_error;
1000 }
1001 /* Map dest */
1002 dst = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED,
1003 fdout, offset);
1004 if (dst == MAP_FAILED) {
1005 if (errno == ENOMEM) {
1006 rv = munmap(src, mapsize);
1007 if (rv != 0) {
1008 goto copy_error;
1009 }
1010 max_mapsize >>= 1;
1011 max_mapsize -= max_mapsize % pagesize;
1012 continue;
1013 }
1014 goto copy_error;
1015 }
1016
1017 /* Copy src to dest */
1018 memcpy(dst, src, mapsize);
1019
1020 /* Close src and dest */
1021 rv = munmap(src, mapsize);
1022 if (rv != 0) {
1023 goto copy_error;
1024 }
1025 rv = munmap(dst, mapsize);
1026 if (rv != 0) {
1027 goto copy_error;
1028 }
1029
1030 offset += mapsize;
1031 size -= mapsize;
1032 }
1033
1034 rv = close(fdin);
1035 fdin = -1;
1036 if (rv == -1) {
1037 goto copy_error;
1038 }
1039
1040 rv = close(fdout);
1041 fdout = -1;
1042 if (rv == -1) {
1043 goto copy_error;
1044 }
1045
1046 return 0;
1047
1048 copy_error:
1049 saveerrno = errno;
1050
1051 if (fdin != -1) {
1052 close (fdin);
1053 }
1054 if (fdout != -1) {
1055 close (fdout);
1056 }
1057 if (src) {
1058 munmap(src, mapsize);
1059 }
1060 if (dst) {
1061 munmap(dst, mapsize);
1062 }
1063 if (fdout != -1 || dst) {
1064 unlink(dest);
1065 }
1066
1067 return saveerrno;
1068 }
1069
1070
1071 /*
1072 * skMoveFile:
1073 * Moves the file "source" to "dest". "Dest" may be a file or a
1074 * directory.
1075 * Input: char * source
1076 * char * dest
1077 * Output: 0 on success, errno on failure.
1078 */
1079 int
skMoveFile(const char * srcPath,const char * destPath)1080 skMoveFile(
1081 const char *srcPath,
1082 const char *destPath)
1083 {
1084 const char *dest;
1085 char destBuf[PATH_MAX];
1086 char base[PATH_MAX];
1087 int rv;
1088 int saveerrno;
1089
1090 /* Handle dest being a directory */
1091 if (skDirExists(destPath)) {
1092 skBasename_r(base, srcPath, sizeof(base));
1093 rv = snprintf(destBuf, sizeof(destBuf), "%s/%s", destPath, base);
1094 if (rv == -1) {
1095 return EIO;
1096 }
1097 if ((unsigned int)rv > (sizeof(destBuf) - 1)) {
1098 return ENAMETOOLONG;
1099 }
1100 dest = destBuf;
1101 } else {
1102 dest = destPath;
1103 }
1104
1105 /* Attempt a simple move */
1106 rv = rename(srcPath, dest);
1107 if (rv == -1) {
1108 if (errno != EXDEV) {
1109 return errno;
1110 }
1111
1112 /* Across filesystems, so copy and delete. */
1113 rv = skCopyFile(srcPath, dest);
1114 if (rv != 0) {
1115 return rv;
1116 }
1117 rv = unlink(srcPath);
1118 if (rv == -1) {
1119 saveerrno = errno;
1120 unlink(dest);
1121 return saveerrno;
1122 }
1123 }
1124
1125 return 0;
1126 }
1127
1128
1129 /* return the temporary directory. */
1130 const char *
skTempDir(const char * user_temp_dir,sk_msg_fn_t err_fn)1131 skTempDir(
1132 const char *user_temp_dir,
1133 sk_msg_fn_t err_fn)
1134 {
1135 const char *tmp_dir = NULL;
1136
1137 /* Use the user's option if given, or SILK_TMPDIR, TMPDIR, or the
1138 * default */
1139 if (NULL == tmp_dir) {
1140 tmp_dir = user_temp_dir;
1141 }
1142 if (NULL == tmp_dir) {
1143 tmp_dir = getenv(SK_TEMPDIR_ENVAR1);
1144 }
1145 if (NULL == tmp_dir) {
1146 tmp_dir = getenv(SK_TEMPDIR_ENVAR2);
1147 }
1148 #ifdef SK_TEMPDIR_DEFAULT
1149 if (NULL == tmp_dir) {
1150 tmp_dir = SK_TEMPDIR_DEFAULT;
1151 }
1152 #endif /* SK_TEMPDIR_DEFAULT */
1153 if (NULL == tmp_dir) {
1154 if (err_fn) {
1155 err_fn("Cannot find a value for the temporary directory.");
1156 }
1157 return NULL;
1158 }
1159 if ( !skDirExists(tmp_dir)) {
1160 if (err_fn) {
1161 err_fn("Temporary directory '%s' does not exist", tmp_dir);
1162 }
1163 return NULL;
1164 }
1165 return tmp_dir;
1166 }
1167
1168
1169 /* paginate the output. see utils.h */
1170 int
skOpenPagerWhenStdoutTty(FILE ** output_stream,char ** pager)1171 skOpenPagerWhenStdoutTty(
1172 FILE **output_stream,
1173 char **pager)
1174 {
1175 FILE *out;
1176 char *pg;
1177 pid_t pid;
1178 int wait_status;
1179
1180 /* verify input; deference the input variables */
1181 assert(output_stream);
1182 assert(pager);
1183 out = *output_stream;
1184 pg = *pager;
1185
1186 /* don't page if output is not "stdout" */
1187 if (NULL == out) {
1188 out = stdout;
1189 } else if (out != stdout) {
1190 /* no change */
1191 return 0;
1192 }
1193
1194 /* don't page a non-terminal */
1195 if ( !FILEIsATty(out)) {
1196 if (pg) {
1197 /* pager explicitly given but ignored */
1198 skAppPrintErr("Ignoring the --pager switch");
1199 }
1200 return 0;
1201 }
1202
1203 /* get pager from environment if not passed in */
1204 if (NULL == pg) {
1205 pg = getenv("SILK_PAGER");
1206 if (NULL == pg) {
1207 pg = getenv("PAGER");
1208 }
1209 }
1210
1211 /* a NULL or an empty string pager means do nothing */
1212 if ((NULL == pg) || ('\0' == pg[0])) {
1213 return 0;
1214 }
1215
1216 #if 1
1217 /* invoke the pager */
1218 out = popen(pg, "w");
1219 if (NULL == out) {
1220 skAppPrintErr("Unable to invoke pager '%s'", pg);
1221 return -1;
1222 }
1223
1224 /* see if pager started. There is a race condition here, and this
1225 * assumes we have only one child, which should be true. */
1226 pid = wait4(0, &wait_status, WNOHANG, NULL);
1227 if (pid) {
1228 skAppPrintErr("Unable to invoke pager '%s'", pg);
1229 return -1;
1230 }
1231 #else
1232 {
1233 int pipe_des[2];
1234
1235 /* create pipe and fork */
1236 if (pipe(pipe_des) == -1) {
1237 skAppPrintErr("Cannot create pipe: %s", strerror(errno));
1238 return -1;
1239 }
1240 pid = fork();
1241 if (pid < 0) {
1242 skAppPrintErr("Cannot fork: %s", strerror(errno));
1243 return -1;
1244 }
1245
1246 if (pid == 0) {
1247 /* CHILD */
1248
1249 /* close output side of pipe; set input to stdin */
1250 close(pipe_des[1]);
1251 if (pipe_des[0] != STDIN_FILENO) {
1252 dup2(pipe_des[0], STDIN_FILENO);
1253 close(pipe_des[0]);
1254 }
1255
1256 /* invoke pager */
1257 execlp(pg, NULL);
1258 skAppPrintErr("Unable to invoke pager '%s': %s",
1259 pg, strerror(errno));
1260 _exit(EXIT_FAILURE);
1261 }
1262
1263 /* PARENT */
1264
1265 /* close input side of pipe */
1266 close(pipe_des[0]);
1267
1268 /* try to open the write side of the pipe */
1269 out = fdopen(pipe_des[1], "w");
1270 if (NULL == out) {
1271 skAppPrintErr("Cannot fdopen: %s", strerror(errno));
1272 return -1;
1273 }
1274
1275 /* it'd be nice to have a slight pause here to give child time to
1276 * die if command cannot be exec'ed, but it's not worth the
1277 * trouble to use select(), and sleep(1) is too long. */
1278
1279 /* see if child died unexpectedly */
1280 if (waitpid(pid, &wait_status, WNOHANG)) {
1281 skAppPrintErr("Unable to invoke pager '%s'", pg);
1282 return -1;
1283 }
1284 }
1285 #endif /* 1: whether to use popen() */
1286
1287 /* looks good. set the output variables and return */
1288 *pager = pg;
1289 *output_stream = out;
1290 return 1;
1291 }
1292
1293
1294 int
skFileptrOpenPager(sk_fileptr_t * file,const char * pager)1295 skFileptrOpenPager(
1296 sk_fileptr_t *file,
1297 const char *pager)
1298 {
1299 FILE *out;
1300 const char *pg;
1301 pid_t pid;
1302 int wait_status;
1303
1304 /* verify input; deference the input variables */
1305 assert(file);
1306
1307 /* don't page if output is not "stdout" */
1308 if (NULL == file->of_fp) {
1309 /* assume output is stdout */
1310 } else if (file->of_fp != stdout) {
1311 /* no change */
1312 return SK_FILEPTR_PAGER_IGNORED;
1313 }
1314
1315 /* don't page a non-terminal */
1316 if (!FILEIsATty(stdout)) {
1317 return SK_FILEPTR_PAGER_IGNORED;
1318 }
1319
1320 /* get pager from environment if not passed in */
1321 if (pager) {
1322 pg = pager;
1323 } else {
1324 pg = getenv("SILK_PAGER");
1325 if (NULL == pg) {
1326 pg = getenv("PAGER");
1327 }
1328 }
1329
1330 /* a NULL or an empty string pager means do nothing */
1331 if ((NULL == pg) || ('\0' == pg[0])) {
1332 return SK_FILEPTR_PAGER_IGNORED;
1333 }
1334
1335 /* invoke the pager */
1336 out = popen(pg, "w");
1337 if (NULL == out) {
1338 return SK_FILEPTR_ERR_POPEN;
1339 }
1340
1341 /* see if pager started. There is a race condition here, and this
1342 * assumes we have only one child, which should be true. */
1343 pid = wait4(0, &wait_status, WNOHANG, NULL);
1344 if (pid) {
1345 pclose(out);
1346 return SK_FILEPTR_ERR_POPEN;
1347 }
1348
1349 /* looks good. set the output variables and return */
1350 file->of_name = (char*)pg;
1351 file->of_fp = out;
1352 file->of_type = SK_FILEPTR_IS_PROCESS;
1353
1354 return SK_FILEPTR_OK;
1355 }
1356
1357
1358 /* Close the pager */
1359 void
skClosePager(FILE * pager_stream,const char * pager)1360 skClosePager(
1361 FILE *pager_stream,
1362 const char *pager)
1363 {
1364 if (pager_stream && (pager_stream != stdout)) {
1365 if (-1 == pclose(pager_stream)) {
1366 skAppPrintErr("Error closing pager '%s'", pager);
1367 }
1368 }
1369 }
1370
1371
1372 /* Get a line from a file */
1373 int
skGetLine(char * out_buffer,size_t buf_size,FILE * stream,const char * comment_start)1374 skGetLine(
1375 char *out_buffer,
1376 size_t buf_size,
1377 FILE *stream,
1378 const char *comment_start)
1379 {
1380 int line_count = 0;
1381 size_t len;
1382 char *eol;
1383
1384 assert(out_buffer && buf_size);
1385
1386 /* read until end of file */
1387 while (!feof(stream)) {
1388 memset(out_buffer, '\0', buf_size);
1389 if (fgets(out_buffer, buf_size, stream) == NULL) {
1390 continue;
1391 }
1392 line_count++;
1393
1394 /* find end of line */
1395 eol = strchr(out_buffer, '\n');
1396 if (out_buffer == eol) {
1397 /* empty line; ignore */
1398 continue;
1399 }
1400
1401 if (eol != NULL) {
1402 /* expected behavior: read an entire line */
1403 *eol = '\0';
1404 } else if (feof(stream)) {
1405 /* okay: last line did not end in newline */
1406 } else {
1407 /* bad: line was longer than buf_size. read
1408 * until newline or eof, then throw away the line */
1409 while (fgets(out_buffer, buf_size, stream)
1410 && !strchr(out_buffer, '\n'))
1411 ; /* empty */
1412 continue;
1413 }
1414
1415 /* Terminate line at first comment char */
1416 if (comment_start && *comment_start
1417 && (NULL != (eol = strstr(out_buffer, comment_start))))
1418 {
1419 if (out_buffer == eol) {
1420 /* only a comment */
1421 continue;
1422 }
1423 *eol = '\0';
1424 }
1425
1426 /* find first non-space character */
1427 len = strspn(out_buffer, " \t\v\f\r");
1428 if ((out_buffer + len) == eol) {
1429 /* whitespace only */
1430 continue;
1431 }
1432
1433 return line_count;
1434 }
1435
1436 out_buffer[0] = '\0';
1437 return 0;
1438 }
1439
1440
1441 /* check that char after % in command is in conversion_chars */
1442 size_t
skSubcommandStringCheck(const char * command,const char * conversion_chars)1443 skSubcommandStringCheck(
1444 const char *command,
1445 const char *conversion_chars)
1446 {
1447 const char *cp;
1448
1449 assert(command);
1450 assert(conversion_chars);
1451
1452 cp = command;
1453 while (NULL != (cp = strchr(cp, (int)'%'))) {
1454 ++cp;
1455 switch (*cp) {
1456 case '%':
1457 break;
1458 case '\0':
1459 return cp - command;
1460 default:
1461 if (NULL == strchr(conversion_chars, (int)*cp)) {
1462 return cp - command;
1463 }
1464 break;
1465 }
1466 ++cp;
1467 }
1468 return 0;
1469 }
1470
1471
1472 /* return a new string that is command with conversions expanded */
1473 char *
skSubcommandStringFill(const char * command,const char * conversion_chars,...)1474 skSubcommandStringFill(
1475 const char *command,
1476 const char *conversion_chars,
1477 ...)
1478 {
1479 char *expanded_cmd;
1480 char *expansion;
1481 const char *cp;
1482 const char *sp;
1483 char *exp_cp;
1484 va_list args;
1485 size_t len;
1486
1487 /* Determine length of buffer needed for the expanded command
1488 * string and allocate it. */
1489 cp = command;
1490 len = 0;
1491 while (NULL != (sp = strchr(cp, (int)'%'))) {
1492 len += sp - cp;
1493 cp = sp + 1;
1494 if ('%' == *cp) {
1495 ++len;
1496 } else {
1497 sp = strchr(conversion_chars, (int)*cp);
1498 if (NULL == sp || '\0' == *sp) {
1499 return NULL;
1500 }
1501 va_start(args, conversion_chars);
1502 do {
1503 expansion = va_arg(args, char *);
1504 --sp;
1505 } while (sp >= conversion_chars);
1506 len += strlen(expansion);
1507 va_end(args);
1508 }
1509 ++cp;
1510 }
1511 len += strlen(cp);
1512 expanded_cmd = (char*)malloc(len + 1);
1513 if (NULL == expanded_cmd) {
1514 return NULL;
1515 }
1516
1517 /* Copy command into buffer, handling %-expansions */
1518 cp = command;
1519 exp_cp = expanded_cmd;
1520 while (NULL != (sp = strchr(cp, (int)'%'))) {
1521 /* copy text we just jumped over */
1522 strncpy(exp_cp, cp, sp - cp);
1523 exp_cp += sp - cp;
1524 cp = sp + 1;
1525 /* handle conversion */
1526 if ('%' == *cp) {
1527 *exp_cp = '%';
1528 ++exp_cp;
1529 } else {
1530 sp = strchr(conversion_chars, (int)*cp);
1531 assert(sp && *sp);
1532 va_start(args, conversion_chars);
1533 do {
1534 expansion = va_arg(args, char *);
1535 --sp;
1536 } while (sp >= conversion_chars);
1537 strcpy(exp_cp, expansion);
1538 exp_cp = strchr(exp_cp, (int)'\0');
1539 va_end(args);
1540 }
1541 ++cp;
1542 assert(len >= (size_t)(exp_cp - expanded_cmd));
1543 }
1544 strcpy(exp_cp, cp);
1545 expanded_cmd[len] = '\0';
1546
1547 return expanded_cmd;
1548 }
1549
1550
1551 /*
1552 * Use the global 'environ' variable to get the environment table.
1553 * On macOS, we must use _NSGetEnviron() to get the environment.
1554 */
1555 #if defined(SK_HAVE_DECL__NSGETENVIRON) && SK_HAVE_DECL__NSGETENVIRON
1556 #include <crt_externs.h>
1557 #define SK_ENVIRON_TABLE *_NSGetEnviron()
1558 #define SK_COPY_ENVIRONMENT 1
1559 #elif defined(SK_HAVE_DECL_ENVIRON) && SK_HAVE_DECL_ENVIRON
1560 #define SK_ENVIRON_TABLE environ
1561 #define SK_COPY_ENVIRONMENT 1
1562 #endif
1563 #ifndef SK_COPY_ENVIRONMENT
1564 #define SK_COPY_ENVIRONMENT 0
1565 #endif
1566
1567
1568 #if SK_COPY_ENVIRONMENT
1569 /**
1570 * Free the copy of the environment that was allocated by
1571 * skSubcommandCopyEnvironment().
1572 */
1573 static void
skSubcommandFreeEnvironment(char ** env_copy)1574 skSubcommandFreeEnvironment(
1575 char **env_copy)
1576 {
1577 size_t i;
1578
1579 if (env_copy) {
1580 for (i = 0; env_copy[i]; ++i) {
1581 free(env_copy[i]);
1582 }
1583 free(env_copy);
1584 }
1585 }
1586
1587 /**
1588 * Make and return a copy of the current environment.
1589 */
1590 static char **
skSubcommandCopyEnvironment(void)1591 skSubcommandCopyEnvironment(
1592 void)
1593 {
1594 char **env = SK_ENVIRON_TABLE;
1595 char **env_copy = NULL;
1596 const size_t step = 100;
1597 size_t sz = 0;
1598 size_t i;
1599
1600 /* Add 1 for the terminating NULL */
1601 for (i = 0; env[i]; ++i) {
1602 if (i == sz) {
1603 char **old = env_copy;
1604 env_copy = (char **)realloc(old, (sz + step + 1) * sizeof(char *));
1605 if (NULL == env_copy) {
1606 old[i] = (char *)NULL;
1607 skSubcommandFreeEnvironment(old);
1608 return NULL;
1609 }
1610 sz += step;
1611 }
1612 env_copy[i] = strdup(env[i]);
1613 if (NULL == env_copy[i]) {
1614 skSubcommandFreeEnvironment(env_copy);
1615 return NULL;
1616 }
1617 }
1618 if (env_copy) {
1619 env_copy[i] = (char *)NULL;
1620 } else {
1621 env_copy = (char **)calloc(1, sizeof(char *));
1622 }
1623 return env_copy;
1624 }
1625 #endif /* SK_COPY_ENVIRONMENT */
1626
1627
1628 /* run the command */
1629 static long int
skSubcommandExecuteHelper(const char * cmd_string,char * const cmd_array[])1630 skSubcommandExecuteHelper(
1631 const char *cmd_string,
1632 char * const cmd_array[])
1633 {
1634 sigset_t sigs;
1635 pid_t pid;
1636
1637 #if SK_COPY_ENVIRONMENT
1638 char **env_copy = skSubcommandCopyEnvironment();
1639 if (NULL == env_copy) {
1640 return -1;
1641 }
1642 #endif /* SK_COPY_ENVIRONMENT */
1643
1644 /* Parent (original process) forks to create Child 1 */
1645 pid = fork();
1646 if (-1 == pid) {
1647 return -1;
1648 }
1649
1650 /* Parent reaps Child 1 and returns */
1651 if (0 != pid) {
1652 #if SK_COPY_ENVIRONMENT
1653 skSubcommandFreeEnvironment(env_copy);
1654 #endif
1655 /* Wait for Child 1 to exit. */
1656 while (waitpid(pid, NULL, 0) == -1) {
1657 if (EINTR != errno) {
1658 return -2;
1659 }
1660 }
1661 return (long)pid;
1662 }
1663
1664 /* Change our process group, so a server program using this
1665 * library that is waiting for any of its children (by process
1666 * group) won't wait for this child. */
1667 setpgid(0, 0);
1668
1669 /* Child 1 forks to create Child 2 */
1670 pid = fork();
1671 if (pid == -1) {
1672 skAppPrintSyserror("Child could not fork for to run command");
1673 _exit(EXIT_FAILURE);
1674 }
1675
1676 /* Child 1 immediately exits, so Parent can stop waiting */
1677 if (pid != 0) {
1678 _exit(EXIT_SUCCESS);
1679 }
1680
1681 /* Only Child 2 makes it here */
1682
1683 /* Unmask signals */
1684 sigemptyset(&sigs);
1685 sigprocmask(SIG_SETMASK, &sigs, NULL);
1686
1687 /* Execute the command */
1688 #if SK_COPY_ENVIRONMENT
1689 if (cmd_string) {
1690 execle("/bin/sh", "sh", "-c", cmd_string, (char*)NULL, env_copy);
1691 } else {
1692 execve(cmd_array[0], cmd_array, env_copy);
1693 }
1694 #else /* SK_COPY_ENVIRONMENT */
1695 if (cmd_string) {
1696 execl("/bin/sh", "sh", "-c", cmd_string, (char*)NULL);
1697 } else {
1698 execv(cmd_array[0], cmd_array);
1699 }
1700 #endif /* SK_COPY_ENVIRONMENT */
1701
1702 /* only get here when an error occurs */
1703 if (cmd_string) {
1704 skAppPrintSyserror("Error invoking /bin/sh");
1705 } else {
1706 skAppPrintSyserror("Error invoking %s", cmd_array[0]);
1707 }
1708 _exit(EXIT_FAILURE);
1709 }
1710
1711
1712 long int
skSubcommandExecute(char * const cmd_array[])1713 skSubcommandExecute(
1714 char * const cmd_array[])
1715 {
1716 return skSubcommandExecuteHelper(NULL, cmd_array);
1717 }
1718
1719
1720 long int
skSubcommandExecuteShell(const char * cmd_string)1721 skSubcommandExecuteShell(
1722 const char *cmd_string)
1723 {
1724 return skSubcommandExecuteHelper(cmd_string, NULL);
1725 }
1726
1727
1728 /*
1729 ** Local Variables:
1730 ** mode:c
1731 ** indent-tabs-mode:nil
1732 ** c-basic-offset:4
1733 ** End:
1734 */
1735