File: cow-shell.c

package info (click to toggle)
cowdancer 0.90
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 648 kB
  • sloc: ansic: 4,593; sh: 407; makefile: 142; cpp: 5
file content (116 lines) | stat: -rw-r--r-- 2,495 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
  Copy-on-write filesystem invocation.

  GPL v2 or later
  Copyright 2005-2009 Junichi Uekawa.
 */
#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>
#include "ilist.h"
#include "log.h"

const char *ilist_PRGNAME = "cow-shell";

static const char *ilistpath = "./.ilist";

/*
 * remove ilist after use.
 */
static void ilist_deleter(const char *ilistfile) {
	if (fork() == 0) {
		/* I am the child process */

		pid_t parent_pid = getppid();
		if (daemon(0, 1) < 0) {
			log_perror("cow-shell daemon");
			exit(-1);
		}
		while (kill(parent_pid, 0) >= 0) {
			sleep(1);
		}
		if (unlink(ilistfile) == -1) {
			log_perror("cow-shell unlink .ilist");
			exit(1);
		}
		exit(0);
	}
}

/**
 * Set environment variables.
 */
static void set_env_vars() {
	char *buf;

	// For sending down as environment variable, use a canonicalized version.
	char *canonicalized_ilistpath = canonicalize_file_name(ilistpath);
	setenv("COWDANCER_ILISTFILE", canonicalized_ilistpath, 1);
	unsetenv("COWDANCER_IGNORE");
	free(canonicalized_ilistpath);

	asprintf(&buf,
			 "%s%s%s",
			 getenv("LD_PRELOAD") ?: "",
			 getenv("LD_PRELOAD") ? " " : "",
			 getenv("COWDANCER_SO") ?: COWDANCER_SO);
	setenv("LD_PRELOAD", buf, 1);
	free(buf);
}

/* give me a command-line to exec,
   and I will cow-keep what's under this directory. */
int main(int ac, char **av) {
	struct stat st;
	int cowdancer_reuse;

	cowdancer_reuse =
		getenv("COWDANCER_REUSE") && !strcmp(getenv("COWDANCER_REUSE"), "yes");

	if (cowdancer_reuse && !stat(ilistpath, &st)) {
		/* if reuse flag is on and file already exists
		   do nothing */
	} else {
		if (unlink(ilistpath) == -1) {
			if (errno == ENOENT) {
				/* expected */
			} else {
				log_perror("cow-shell: unlink of .ilist failed");
				return 1;
			}
		}
		if (ilistcreate(ilistpath, NULL)) {
			ilist_outofmemory(".ilist creation failed");
			return 1;
		}
	}

	set_env_vars();

	if (!cowdancer_reuse) {
		/* if reuse flag is not on, remove the file */
		ilist_deleter(ilistpath);
	}

	if (ac > 1) {
		execvp(av[1], av + 1);
	} else {
		const char *myshell = getenv("SHELL") ?: "/bin/sh";
		log_printf(log_info, "Invoking %s", myshell);

		execlp(myshell, myshell, NULL);
		log_perror("cow-shell: exec");

		log_printf(log_warn, "Falling back to /bin/sh");
		execlp("/bin/sh", "/bin/sh", NULL);
	}
	log_perror("cow-shell: exec");
	return 1;
}