AtlatestRepositorysigil-wm
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_SOURCE5
#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>14
static void fail(const char *message) {15
fprintf(stderr,"environment-exec: %s\n",message);16
exit(126);17
}19
int 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 signals60
* 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;73
}