AtlatestRepositorysigil-wm

sigil-wm / tree / src / cenvironment-exec.c

1/* SPDX-License-Identifier: BSD-3-Clause */
2/* Replace an exec-only child's environment without interpreting its data.
3 * Values arrive in a private NUL file or numbered environment entries. */
4#define _GNU_SOURCE
5#include <errno.h>
6#include <fcntl.h>
7#include <signal.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/stat.h>
12#include <unistd.h>
14static void fail(const char *message) {
15 fprintf(stderr,"environment-exec: %s\n",message);
16 exit(126);
19int main(int argc,char **argv) {
20 if (argc<5) fail("invalid invocation");
21 char *end;
22 errno=0;
23 long count=strtol(argv[2],&end,10);
24 if (errno || *end || count<0 || count>argc-5 ||
25 strcmp(argv[3+count],"--")) fail("invalid override count");
26 char **values=calloc((size_t)count+1,sizeof(*values));
27 if (!values) fail("allocation failed");
28 for (long i=0;i<count;i++) {
29 char key[64];
30 snprintf(key,sizeof(key),"SIGIL_EXEC_VALUE_%ld",i);
31 const char *value=getenv(key);
32 if (!value || !(values[i]=strdup(value))) fail("missing override");
33 }
34 int fd=open(argv[1],O_RDONLY|O_CLOEXEC|O_NOFOLLOW);
35 struct stat status;
36 if (fd<0 || fstat(fd,&status) || !S_ISREG(status.st_mode) ||
37 status.st_uid!=geteuid() || (status.st_mode&077) ||
38 status.st_size>16*1024*1024) fail("invalid private snapshot");
39 FILE *input=fdopen(fd,"r");
40 if (!input || clearenv()) fail("cannot replace environment");
41 char *entry=NULL;
42 size_t capacity=0;
43 ssize_t length;
44 while ((length=getdelim(&entry,&capacity,0,input))>=0) {
45 if (!length || entry[length-1]) fail("unterminated snapshot entry");
46 char *separator=strchr(entry,'=');
47 if (!separator || separator==entry) fail("invalid snapshot entry");
48 *separator=0;
49 if (setenv(entry,separator+1,1)) fail("cannot restore entry");
50 }
51 if (ferror(input)) fail("cannot read snapshot");
52 fclose(input);
53 free(entry);
54 for (long i=0;i<count;i++) {
55 if (setenv(argv[3+i],values[i],1)) fail("cannot apply override");
56 free(values[i]);
57 }
58 free(values);
59 /* This exec-only child must not export its launcher's ignored signals
60 * or blocked mask to the client. Unsupported signal numbers are skipped. */
61 struct sigaction action={.sa_handler=SIG_DFL};
62 if(sigemptyset(&action.sa_mask)) fail("cannot initialize signal defaults");
63 for(int signum=1;signum<NSIG;signum++) {
64 if(signum==SIGKILL || signum==SIGSTOP) continue;
65 if(sigaction(signum,&action,NULL) && errno!=EINVAL)
66 fail("cannot restore signal disposition");
67 }
68 sigset_t unblocked;
69 if(sigemptyset(&unblocked) || sigprocmask(SIG_SETMASK,&unblocked,NULL))
70 fail("cannot restore signal mask");
71 execvp(argv[4+count],argv+4+count);
72 return errno==ENOENT ? 127 : 126;