-
Notifications
You must be signed in to change notification settings - Fork 30
/
mklaunchdplist.c
283 lines (256 loc) · 9.85 KB
/
mklaunchdplist.c
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/*-
* xnumon - monitor macOS for malicious activity
* https://www.roe.ch/xnumon
*
* Copyright (c) 2017-2019, Daniel Roethlisberger <daniel@roe.ch>.
* All rights reserved.
*
* Licensed under the Open Software License version 3.0.
*/
/*
* Code to generate a launchd plist for xnumon, used as part of the build
* process. In c because original intent was to make xnumon self-installing.
* This should probably be moved into a script.
*
* Resources:
*
* Daemons and Services Programming Guide
* https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup
*
* TN2083: Daemons and Agents
* https://developer.apple.com/library/content/technotes/tn2083/
*
* man launchd.plist
*
* http://www.launchd.info/
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef __BSD__
#include <getopt.h>
#endif /* !__BSD__ */
/*
* Documentation on the keys within this function was taken from the
* launchd.plist manual page.
*/
static int
launchd_plist_write(const char *label, const char *targetdir,
const char *execpath, int argc, char *argv[]) {
char *plist;
FILE *f;
if (!targetdir)
targetdir = "/Library/LaunchDaemons";
if (asprintf(&plist, "%s/%s.plist", targetdir, label) == -1)
return -1;
(void)unlink(plist);
f = fopen(plist, "w");
free(plist);
if (!f)
return -1;
fchmod(fileno(f), 0600);
fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC"
" \"-//Apple Computer//DTD PLIST 1.0//EN\""
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
"<dict>\n");
/* This required key uniquely identifies the job to launchd. */
fprintf(f, "\t<key>Label</key>\n"
"\t<string>%s</string>\n", label);
/* This key maps to the first argument of execv(3) and indicates the
* absolute path to the executable for the job. If this key is
* missing, then the first element of the array of strings provided to
* the ProgramArguments will be used instead. This key is required in
* the absence of the ProgramArguments key. */
fprintf(f, "\t<key>Program</key>\n"
"\t<string>%s</string>\n", execpath);
/* This key maps to the second argument of execvp(3) and specifies the
* argument vector to be passed to the job when a process is spawned.
* This key is required in the absence of the Program key. */
fprintf(f, "\t<key>ProgramArguments</key>\n"
"\t<array>\n");
for (int i = 0; i < argc; i++) {
fprintf(f, "\t\t<string>%s</string>\n", argv[i]);
}
fprintf(f, "\t</array>\n");
/* This optional key is used to control whether your job is launched
* once at the time the job is loaded. The default is false. This key
* should be avoided, as speculative job launches have an adverse
* effect on system- boot and user-login scenarios. */
fprintf(f, "\t<key>RunAtLoad</key>\n"
"\t<true/>\n");
/* This optional key is used to control whether your job is to be kept
* continuously running or to let demand and conditions control the
* invocation. The default is false and therefore only demand will
* start the job. The value may be set to true to unconditionally keep
* the job alive. Alternatively, a dictionary of conditions may be
* specified to selectively control whether launchd keeps a job alive
* or not. If multiple keys are provided, launchd ORs them, thus
* providing maximum flexibility to the job to refine the logic and
* stall if necessary. If launchd finds no reason to restart the job,
* it falls back on demand based invocation. Jobs that exit quickly
* and frequently when configured to be kept alive will be throttled to
* conserve system resources. */
fprintf(f, "\t<key>KeepAlive</key>\n"
"\t<true/>\n");
/* This key lets one override the default throttling policy imposed on
* jobs by launchd. The value is in seconds, and by default, jobs will
* not be spawned more than once every 10 seconds. The principle
* behind this is that jobs should linger around just in case they are
* needed again in the near future. This not only reduces the latency
* of responses, but it encourages developers to amortize the cost of
* program invocation. */
fprintf(f, "\t<key>ThrottleInterval</key>\n"
"\t<integer>60</integer>\n");
/* The amount of time launchd waits between sending the SIGTERM signal
* and before sending a SIGKILL signal when the job is to be stopped.
* The default value is system-defined. The value zero is interpreted
* as infinity and should not be used, as it can stall system shutdown
* forever. */
fprintf(f, "\t<key>ExitTimeOut</key>\n"
"\t<integer>60</integer>\n");
/* This optional key describes, at a high level, the intended purpose
* of the job. The system will apply resource limits based on what
* kind of job it is. If left unspecified, the system will apply light
* resource limits to the job, throttling its CPU usage and I/O
* bandwidth. This classification is preferable to using the
* HardResourceLimits, SoftResourceLimits and Nice keys.
*
* Interactive jobs run with the same resource limitations as apps,
* that is to say, none. Interactive jobs are critical to maintaining a
* responsive user experience, and this key should only be used if an
* app's ability to be responsive depends on it, and cannot be made
* Adaptive. */
fprintf(f, "\t<key>ProcessType</key>\n"
"\t<string>Interactive</string>\n");
#if 0
/* This optional key specifies what nice(3) value should be applied to
* the daemon. */
fprintf(f, "\t<key>Nice</key>\n"
"\t<integer>-5</integer>\n");
#endif
/* Resource limits to be imposed on the job. These adjust variables
* set with setrlimit(2).
*
* Core <integer>
* The largest size (in bytes) core file that may be created. */
#ifndef NDEBUG
fprintf(f, "\t<key>SoftResourceLimits</key>\n"
"\t<dict><key>Core</key><integer>-1</integer></dict>\n");
fprintf(f, "\t<key>HardResourceLimits</key>\n"
"\t<dict><key>Core</key><integer>-1</integer></dict>\n");
#endif
/* This optional key is used to specify a directory to chdir(2) to
* before running the job. */
fprintf(f, "\t<key>WorkingDirectory</key>\n"
"\t<string>/</string>\n");
#if 0
/* This optional key specifies the user to run the job as. This key is
* only applicable for services that are loaded into the privileged
* system domain. */
fprintf(f, "\t<key>UserName</key>\n"
"\t<string>root</string>\n");
/* This optional key specifies the group to run the job as. This key
* is only applicable for services that are loaded into the privileged
* system domain. If UserName is set and GroupName is not, then the
* group will be set to the primary group of the user. */
fprintf(f, "\t<key>GroupName</key>\n"
"\t<string>admin</string>\n");
/* This optional key specifies whether initgroups(3) to initialize the
* group list for the job. The default is true. This key will be
* ignored if the UserName key is not set. Note that for agents, the
* UserName key is ignored. */
fprintf(f, "\t<key>InitGroups</key>\n"
"\t<true/>\n");
#endif
#if 0
/* This optional key specifies that the given path should be mapped to
* the job's stdout(4), and that any writes to the job's stdout(4) will
* go to the given file. If the file does not exist, it will be created
* with writable permissions and ownership reflecting the user and/or
* group specified as the UserName and/or GroupName, respectively (if
* set) and permissions reflecting the umask(2) specified by the Umask
* key, if set. */
fprintf(f, "\t<key>StandardOutPath</key>\n"
"\t<string>%s</string>\n", stdoutpath);
#endif
#if 0
/* This optional key specifies that the given path should be mapped to
* the job's stderr(4), and that any writes to the job's stderr(4) will
* go to the given file. Note that this file is opened as readable and
* writable as mandated by the POSIX specification for unclear reasons.
* If the file does not exist, it will be created with ownership
* reflecting the user and/or group specified as the UserName and/or
* GroupName, respectively (if set) and permissions reflecting the
* umask(2) specified by the Umask key, if set. */
fprintf(f, "\t<key>StandardErrorPath</key>\n"
"\t<string>%s</string>\n", stderrpath);
#endif
fprintf(f, "</dict>\n"
"</plist>\n");
fclose(f);
return 0;
}
static void
fusage(FILE *f, const char *argv0) {
fprintf(f,
"Usage: %s -l label -d targetdir -e execpath -- [xnumon options]\n"
" %s -h\n"
" -l label basename of plist, usually reverse DNS ID\n"
" -d targetdir target directory to write plist file into\n"
" -e execpath path to the xnumon binary to be launched by the plist\n"
" -h print usage and exit\n"
, argv0, argv0);
}
int
main(int argc, char *argv[]) {
int ch;
char *label = NULL;
char *targetdir = NULL;
char *execpath = NULL;
while ((ch = getopt(argc, argv, "l:d:e:h")) != -1) {
switch (ch) {
case 'l':
label = strdup(optarg);
break;
case 'd':
targetdir = strdup(optarg);
break;
case 'e':
execpath = strdup(optarg);
break;
case 'h':
fusage(stdout, argv[0]);
exit(EXIT_SUCCESS);
case '?':
exit(EXIT_FAILURE);
default:
fusage(stderr, argv[0]);
exit(EXIT_FAILURE);
}
}
if (!label || !targetdir || !execpath) {
fusage(stderr, argv[0]);
exit(EXIT_FAILURE);
}
argc -= optind;
argv += optind;
int ac = argc + 1;
char **av = malloc(ac * sizeof(char *));
av[0] = strdup(execpath);
for (int i = 1; i < ac; i++)
av[i] = strdup(argv[i-1]);
launchd_plist_write(label, targetdir, execpath, ac, av);
for (int i = 0; i < ac; i++)
free(av[i]);
free(av);
free(label);
free(targetdir);
free(execpath);
exit(EXIT_SUCCESS);
}