2 * Copyright (c) 2019-2020 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
20 * 3. Neither the name of The DragonFly Project nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific, prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 struct logerrinfo
*next
;
49 static buildenv_t
**BuildEnvTail
= &BuildEnv
;
51 extern char **environ
;
53 static void *dexec_logerr_thread(void *info
);
55 #define EINFO_HSIZE 1024
56 #define EINFO_HMASK (EINFO_HSIZE - 1)
58 static struct logerrinfo
*EInfo
[EINFO_HSIZE
];
59 static pthread_t ETid
;
61 static pthread_cond_t ECond
;
67 return(&EInfo
[pid
& EINFO_HMASK
]);
71 _dfatal(const char *file __unused
, int line __unused
, const char *func
,
72 int do_errno
, const char *ctl
, ...)
76 fprintf(stderr
, "%s: ", func
);
78 vfprintf(stderr
, ctl
, va
);
81 fprintf(stderr
, ": %s", strerror(errno
));
82 fprintf(stderr
, "\n");
86 kill(getpid(), SIGQUIT
);
91 _ddprintf(int tab
, const char *ctl
, ...)
96 printf("%*.*s", tab
, tab
, "");
98 vfprintf(stdout
, ctl
, va
);
103 strdup_or_null(char *str
)
110 static const char *DLogNames
[] = {
111 "00_last_results.log",
112 "01_success_list.log",
113 "02_failure_list.log",
114 "03_ignored_list.log",
115 "04_skipped_list.log",
116 "05_abnormal_command_output.log",
117 "06_obsolete_packages.log",
121 static int DLogFd
[DLOG_COUNT
];
122 static pthread_mutex_t DLogFdMutex
;
124 #define arysize(ary) (sizeof((ary)) / sizeof((ary)[0]))
127 dlogfd(int which
, int modes
)
133 if ((fd
= DLogFd
[which
]) > 0)
135 pthread_mutex_lock(&DLogFdMutex
);
136 if ((fd
= DLogFd
[which
]) <= 0) {
137 asprintf(&path
, "%s/%s", LogsPath
, DLogNames
[which
]);
138 fd
= open(path
, modes
, 0666);
142 pthread_mutex_unlock(&DLogFdMutex
);
153 ddassert(DLOG_COUNT
== arysize(DLogNames
));
154 for (i
= 0; i
< DLOG_COUNT
; ++i
) {
159 (void)dlogfd(i
, O_RDWR
|O_CREAT
|O_TRUNC
|O_APPEND
);
164 _dlog(int which
, const char *ctl
, ...)
176 ddassert((uint
)which
< DLOG_COUNT
);
178 vasprintf(&buf
, ctl
, va
);
183 * The special sequence ## right-justfies the text after the ##.
185 * NOTE: Alignment test includes \n so use 80 instead of 79 to
186 * leave one char unused on a 80-column terminal.
188 if ((ptr
= strstr(buf
, "##")) != NULL
) {
194 l1
= (int)(ptr
- buf
);
199 asprintf(&b2
, "%s%*.*s%s",
200 buf
, spc
, spc
, "", ptr
+ 2);
203 asprintf(&b2
, "%s%s", buf
, ptr
+ 2);
211 * All logs also go to log 00.
213 if (which
!= DLOG_ALL
) {
214 fd
= dlogfd(DLOG_ALL
, O_RDWR
|O_CREAT
|O_APPEND
);
222 fd
= dlogfd(which
, O_RDWR
|O_CREAT
|O_APPEND
);
226 * If ncurses is not being used, all log output also goes
227 * to stdout, unless filtered.
229 if ((UseNCurses
== 0 || (filter
& DLOG_STDOUT
)) &&
230 (filter
& DLOG_FILTER
) == 0) {
232 if (filter
& DLOG_GRN
)
233 write(1, "\x1b[0;32m", 7);
234 if (filter
& DLOG_RED
)
235 write(1, "\x1b[0;31m", 7);
238 if (ColorOpt
&& (filter
& (DLOG_GRN
|DLOG_RED
))) {
239 write(1, "\x1b[0;39m", 7);
248 return(dlogfd(DLOG_ALL
, O_RDWR
|O_CREAT
|O_APPEND
));
252 * Bulk and Build environment control. These routines are only called
253 * unthreaded or when dsynth threads are idle.
256 addbuildenv(const char *label
, const char *data
, int type
)
260 env
= calloc(1, sizeof(*env
));
261 env
->a1
= strdup(label
);
262 env
->a2
= strdup(data
);
263 env
->label
= env
->a1
;
267 BuildEnvTail
= &env
->next
;
271 delbuildenv(const char *label
)
277 while ((env
= *envp
) != NULL
) {
278 if (strcmp(env
->label
, label
) == 0) {
293 getbuildenv(const char *label
)
299 while ((env
= *envp
) != NULL
) {
300 if (strcmp(env
->label
, label
) == 0)
308 freestrp(char **strp
)
320 *strp
= strdup(*strp
);
324 ipcreadmsg(int fd
, wmsg_t
*msg
)
331 ptr
= (char *)(void *)msg
;
333 r
= read(fd
, ptr
, res
);
346 ipcwritemsg(int fd
, wmsg_t
*msg
)
353 ptr
= (char *)(void *)msg
;
355 r
= write(fd
, ptr
, res
);
368 askyn(const char *ctl
, ...)
382 if (fgets(buf
, sizeof(buf
), stdin
) == NULL
)
384 if (buf
[0] == 'y' || buf
[0] == 'Y') {
388 if (buf
[0] == 'n' || buf
[0] == 'N') {
392 printf("Please type y/n\n");
398 * Get swap% used 0.0-1.0.
400 * NOTE: This routine is intended to return quickly.
402 * NOTE: swap_cache (caching for hard drives) is persistent and should
403 * not be counted for our purposes.
406 getswappct(int *noswapp
)
410 long swap_cache __unused
= 0;
414 len
= sizeof(swap_size
);
415 sysctlbyname("vm.swap_size", &swap_size
, &len
, NULL
, 0);
416 len
= sizeof(swap_size
);
417 sysctlbyname("vm.swap_anon_use", &swap_anon
, &len
, NULL
, 0);
418 len
= sizeof(swap_size
);
419 sysctlbyname("vm.swap_cache_use", &swap_cache
, &len
, NULL
, 0);
421 dswap
= (double)(swap_anon
/*+swap_cache*/) / (double)swap_size
;
431 * dexec_open()/fgets/dexec_close()
433 * Similar to popen() but directly exec()s the argument list (cav[0] must
434 * be an absolute path).
436 * If xenv is non-NULL its an array of local buildenv_t's to be used.
437 * The array is terminated with a NULL xenv->label.
439 * If with_env is non-zero the configured environment is included.
441 * If with_mvars is non-zero the make environment is passed as VAR=DATA
442 * elements on the command line.
445 dexec_open(const char *logid
, const char **cav
, int cac
,
446 pid_t
*pidp
, buildenv_t
*xenv
, int with_env
, int with_mvars
)
450 char *allocary
[MAXCAC
*2];
455 int fds
[2]; /* stdout */
458 struct logerrinfo
*einfo
;
462 * Error logging thread setup
465 pthread_mutex_lock(&DLogFdMutex
);
467 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, EFds
) < 0)
468 dfatal_errno("socketpair");
471 if (setsockopt(EFds
[0], SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
)) < 0) {
474 fprintf(stderr
, "SO_PASSCRED not supported\n");
479 pthread_cond_init(&ECond
, NULL
);
480 pthread_create(&ETid
, NULL
, dexec_logerr_thread
, NULL
);
482 pthread_mutex_unlock(&DLogFdMutex
);
488 * For now we are ignoring the passed-in environment, so don't
489 * copy the existing environment into the exec environment.
491 while (environ
[env_basei
])
494 cenv
= calloc(env_basei
+ MAXCAC
, sizeof(char *));
495 for (envi
= 0; envi
< env_basei
; ++envi
)
496 cenv
[envi
] = environ
[envi
];
499 for (benv
= BuildEnv
; benv
; benv
= benv
->next
) {
501 (benv
->type
& BENV_CMDMASK
) == BENV_MAKECONF
) {
502 asprintf(&allocary
[alloci
], "%s=%s",
503 benv
->label
, benv
->data
);
504 cav
[cac
++] = allocary
[alloci
];
508 (benv
->type
& BENV_PKGLIST
) &&
509 (benv
->type
& BENV_CMDMASK
) == BENV_ENVIRONMENT
) {
510 asprintf(&allocary
[alloci
], "%s=%s",
511 benv
->label
, benv
->data
);
512 cenv
[envi
++] = allocary
[alloci
];
515 ddassert(cac
< MAXCAC
&& envi
- env_basei
< MAXCAC
);
519 * Extra environment specific to this particular dexec
521 while (xenv
&& xenv
->label
) {
522 asprintf(&allocary
[alloci
], "%s=%s",
523 xenv
->label
, xenv
->data
);
524 cenv
[envi
++] = allocary
[alloci
];
532 if (pipe2(fds
, O_CLOEXEC
) < 0)
533 dfatal_errno("pipe");
534 nullfd
= open("/dev/null", O_RDWR
| O_CLOEXEC
);
536 dfatal_errno("open(\"/dev/null\")");
539 * We have to be very careful using vfork(), do only the bare
540 * minimum necessary in the child to set it up and exec it.
546 printf("%s", cav
[0]);
547 for (i
= 0; cav
[i
]; ++i
)
548 printf(" %s", cav
[i
]);
551 for (i
= 0; cenv
[i
]; ++i
)
552 printf(" %s", cenv
[i
]);
563 close(fds
[0]); /* safety */
564 close(EFds
[0]); /* safety */
565 dup2(nullfd
, 0); /* no questions! */
566 closefrom(3); /* be nice */
569 * Self-nice to be nice (ignore any error)
572 setpriority(PRIO_PROCESS
, 0, NiceOpt
);
575 * Throw in some capability restrictions
577 set_capability_restrictions();
579 execve(cav
[0], (void *)cav
, (void *)cenv
);
580 write(2, "EXEC FAILURE\n", 13);
587 dfatal_errno("vfork failed");
589 fp
= fdopen(fds
[0], "r");
595 einfo
= calloc(1, sizeof(*einfo
));
597 einfo
->logid
= strdup(logid
);
599 pthread_mutex_lock(&DLogFdMutex
);
601 einfo
->next
= *einfohash(pid
);
602 *einfohash(pid
) = einfo
;
603 pthread_cond_signal(&ECond
);
604 pthread_mutex_unlock(&DLogFdMutex
);
606 while (--alloci
>= 0)
607 free(allocary
[alloci
]);
614 dexec_close(FILE *fp
, pid_t pid
)
616 struct logerrinfo
**einfop
;
617 struct logerrinfo
*einfo
;
622 while ((rpid
= waitpid(pid
, &status
, 0)) != pid
) {
630 pthread_mutex_lock(&DLogFdMutex
);
631 einfop
= einfohash(pid
);
632 while ((einfo
= *einfop
) != NULL
) {
633 if (einfo
->pid
== pid
) {
637 einfop
= &einfo
->next
;
639 pthread_mutex_unlock(&DLogFdMutex
);
641 return (WEXITSTATUS(status
));
645 dexec_logerr_thread(void *dummy __unused
)
649 char cbuf
[CMSG_SPACE(sizeof(struct ucred
))];
650 struct cmsghdr cbuf_align
;
652 struct cmsghdr
*cmsg
;
653 struct logerrinfo
**einfop
;
654 struct logerrinfo
*einfo
;
658 int mflags
= MSG_DONTWAIT
;
661 pthread_detach(pthread_self());
667 msg
.msg_control
= cmsg_buf
.cbuf
;
668 msg
.msg_controllen
= sizeof(cmsg_buf
.cbuf
);
676 iov
.iov_len
= sizeof(buf
) - 1;
679 * Wait for message, if none are pending then clean-up
680 * exited einfos (this ensures that we have flushed all
681 * error output before freeing the structure).
683 * Don't obtain the mutex until we really need it. The
684 * parent thread can only 'add' einfo entries to the hash
685 * table so we are basically scan-safe.
687 len
= recvmsg(fd
, &msg
, mflags
);
689 if (errno
!= EAGAIN
) { /* something messed up */
690 fprintf(stderr
, "ERRNO %d\n", errno
);
694 for (i
= 0; i
< EINFO_HSIZE
; ++i
) {
703 pthread_mutex_lock(&DLogFdMutex
);
705 while ((einfo
= *einfop
) != NULL
) {
707 *einfop
= einfo
->next
;
712 einfop
= &einfo
->next
;
715 pthread_mutex_unlock(&DLogFdMutex
);
722 * Process SCM_CREDS, if present. Throw away SCM_RIGHTS
723 * if some sub-process stupidly sent it.
726 mflags
= MSG_DONTWAIT
;
728 if (len
&& buf
[len
-1] == '\n')
732 for (cmsg
= CMSG_FIRSTHDR(&msg
);
734 cmsg
= CMSG_NXTHDR(&msg
, cmsg
)) {
735 struct cmsgcred
*cred
;
739 if (cmsg
->cmsg_level
!= SOL_SOCKET
)
742 switch(cmsg
->cmsg_type
) {
745 cred
= (void *)CMSG_DATA(cmsg
);
747 einfo
= *einfohash(cred
->cmcred_pid
);
748 while (einfo
&& einfo
->pid
!= cred
->cmcred_pid
)
752 fds
= (void *)CMSG_DATA(cmsg
);
753 n
= (cmsg
->cmsg_len
- sizeof(cmsg
)) /
755 for (i
= 0; i
< n
; ++i
)
761 if (einfo
&& einfo
->logid
) {
762 dlog(DLOG_ALL
| DLOG_STDOUT
,
766 dlog(DLOG_ALL
| DLOG_STDOUT
, "%s", buf
);
773 getphasestr(worker_phase_t phaseid
)
781 case PHASE_INSTALL_PKGS
:
782 phase
= "install-pkgs";
784 case PHASE_CHECK_SANITY
:
785 phase
= "check-sanity";
787 case PHASE_PKG_DEPENDS
:
788 phase
= "pkg-depends";
790 case PHASE_FETCH_DEPENDS
:
791 phase
= "fetch-depends";
799 case PHASE_EXTRACT_DEPENDS
:
800 phase
= "extract-depends";
805 case PHASE_PATCH_DEPENDS
:
806 phase
= "patch-depends";
811 case PHASE_BUILD_DEPENDS
:
812 phase
= "build-depends";
814 case PHASE_LIB_DEPENDS
:
815 phase
= "lib-depends";
817 case PHASE_CONFIGURE
:
823 case PHASE_RUN_DEPENDS
:
824 phase
= "run-depends";
832 case PHASE_CHECK_PLIST
:
833 phase
= "check-plist";
841 case PHASE_DEINSTALL
:
850 case PHASE_SHOW_CONFIG
:
851 phase
= "show-config";
853 case PHASE_DUMP_MAKECONF
:
857 phase
= "Run-Unknown";
864 readlogline(monitorlog_t
*log
, char **bufp
)
870 * Reset buffer as an optimization to avoid unnecessary
874 if (log
->buf_beg
== log
->buf_end
) {
881 * Look for newline, handle discard mode
884 for (n
= log
->buf_scan
; n
< log
->buf_end
; ++n
) {
885 if (log
->buf
[n
] == '\n') {
886 *bufp
= log
->buf
+ log
->buf_beg
;
887 r
= n
- log
->buf_beg
;
888 log
->buf_beg
= n
+ 1;
889 log
->buf_scan
= n
+ 1;
891 if (log
->buf_discard_mode
== 0)
893 log
->buf_discard_mode
= 0;
901 if (n
== sizeof(log
->buf
)) {
904 * Shift the buffer to make room and read more data.
906 bcopy(log
->buf
+ log
->buf_beg
,
909 log
->buf_end
-= log
->buf_beg
;
910 log
->buf_scan
-= log
->buf_beg
;
913 } else if (log
->buf_discard_mode
) {
915 * Overflow. If in discard mode just throw it all
916 * away. Stay in discard mode.
923 * Overflow. If not in discard mode return a truncated
924 * line and enter discard mode.
926 * The caller will temporarily set ptr[r] = 0 so make
927 * sure that does not overflow our buffer as we are not
930 * (log->buf_beg is 0);
932 *bufp
= log
->buf
+ log
->buf_beg
;
936 log
->buf_discard_mode
= 1;
943 * Read more data. If there is no data pending then return -1,
944 * otherwise loop up to see if more complete line(s) are available.
947 log
->buf
+ log
->buf_end
,
948 sizeof(log
->buf
) - log
->buf_end
,
958 crcDirTree(const char *path
)
968 pav
[0] = strdup(path
);
971 fts
= fts_open(pav
, FTS_PHYSICAL
| FTS_NOCHDIR
, NULL
);
974 while ((fen
= fts_read(fts
)) != NULL
) {
975 if (fen
->fts_info
!= FTS_F
&& fen
->fts_info
!= FTS_SL
)
978 * Ignore hidden dot files or ones ending with .core from the
979 * calculated CRC sum to prevent unnecessary rebuilds.
981 if (fen
->fts_name
[0] == '.')
983 if (fen
->fts_namelen
>= 5 &&
984 !strcmp(fen
->fts_name
+ fen
->fts_namelen
- 5, ".core")) {
990 val
= iscsi_crc32(&st
->st_mtime
, sizeof(st
->st_mtime
));
991 val
= iscsi_crc32_ext(&st
->st_size
, sizeof(st
->st_size
), val
);
992 val
= iscsi_crc32_ext(fen
->fts_path
, fen
->fts_pathlen
, val
);
1003 set_capability_restrictions(void)
1005 if (CapabilityRestrictions
) {
1006 syscap_set(SYSCAP_RESTRICTEDROOT
, __SYSCAP_ALL
,
1008 syscap_set(SYSCAP_SENSITIVEROOT
, __SYSCAP_ALL
,
1010 syscap_set(SYSCAP_NONET_SENSITIVE
, __SYSCAP_ALL
,
1012 syscap_set(SYSCAP_NOVFS_SENSITIVE
, __SYSCAP_ALL
,
1014 syscap_set(SYSCAP_NOMOUNT
, __SYSCAP_ALL
,
1016 syscap_set(SYSCAP_NOJAIL
, __SYSCAP_ALL
,
1018 syscap_set(SYSCAP_NONET_RESPORT
, __SYSCAP_ALL
,
1020 syscap_set(SYSCAP_NONET_RAW
, __SYSCAP_ALL
,