xref: /haiku/src/bin/launch_roster.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
1 /*
2  * Copyright 2015-2018, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 /*!	The launch_daemon's companion command line tool. */
8 
9 
10 #include <LaunchRoster.h>
11 #include <StringList.h>
12 
13 #include <errno.h>
14 #include <getopt.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <time.h>
19 
20 
21 static struct option const kLongOptions[] = {
22 	{"verbose", no_argument, 0, 'v'},
23 	{"help", no_argument, 0, 'h'},
24 	{NULL}
25 };
26 
27 static struct option const kLogLongOptions[] = {
28 	{"help", no_argument, 0, 'h'},
29 	{"raw", no_argument, 0, 'r'},
30 	{"user", no_argument, 0, 'u'},
31 	{"system", no_argument, 0, 's'},
32 	{"event", required_argument, 0, 'e'},
33 	{"limit", required_argument, 0, 'l'},
34 	{NULL}
35 };
36 
37 extern const char *__progname;
38 static const char *kProgramName = __progname;
39 
40 
41 static void
42 list_jobs(bool verbose)
43 {
44 	BLaunchRoster roster;
45 	BStringList jobs;
46 	status_t status = roster.GetJobs(NULL, jobs);
47 	if (status != B_OK) {
48 		fprintf(stderr, "%s: Could not get job listing: %s\n", kProgramName,
49 			strerror(status));
50 		exit(EXIT_FAILURE);
51 	}
52 
53 	for (int32 i = 0; i < jobs.CountStrings(); i++)
54 		puts(jobs.StringAt(i).String());
55 }
56 
57 
58 static void
59 list_targets(bool verbose)
60 {
61 	BLaunchRoster roster;
62 	BStringList targets;
63 	status_t status = roster.GetTargets(targets);
64 	if (status != B_OK) {
65 		fprintf(stderr, "%s: Could not get target listing: %s\n", kProgramName,
66 			strerror(status));
67 		exit(EXIT_FAILURE);
68 	}
69 
70 	for (int32 i = 0; i < targets.CountStrings(); i++)
71 		puts(targets.StringAt(i).String());
72 }
73 
74 
75 static void
76 print_log(const BMessage& log)
77 {
78 	time_t now = time(NULL);
79 	bigtime_t runtime = system_time();
80 
81 	for (int32 index = 0;; index++) {
82 		BMessage item;
83 		if (log.FindMessage("item", index, &item) != B_OK)
84 			break;
85 
86 		uint64 when;
87 		const char* message;
88 		if (item.FindUInt64("when", &when) != B_OK
89 			|| item.FindString("message", &message) != B_OK)
90 			break;
91 
92 		time_t at = now - (runtime - when) / 1000000l;
93 		struct tm tm;
94 		localtime_r(&at, &tm);
95 		char label[256];
96 		strftime(label, sizeof(label), "%F %X", &tm);
97 		printf("%s %s\n", label, message);
98 	}
99 }
100 
101 
102 static void
103 log_usage(int status)
104 {
105 	fprintf(stderr, "Usage: %s log [-rusel] [<job-name>]\n"
106 		"Where the following options are allowed:\n"
107 		"  -u --user       List only user log entries\n"
108 		"  -s --system     List only system log entries\n"
109 		"  -e --event      Filter by event name (partial names accepted)\n"
110 		"  -l --limit <n>  Limit output to <n> events\n"
111 		"<job-name>, if given, filters the jobs by name.\n",
112 		kProgramName);
113 
114 	exit(status);
115 }
116 
117 
118 static void
119 get_log(int argCount, char** args)
120 {
121 	bool raw = false;
122 	bool userOnly = false;
123 	bool systemOnly = false;
124 	int32 limit = 0;
125 	const char* event = NULL;
126 	const char* job = NULL;
127 
128 	optind = 0;
129 	int c;
130 	while ((c = getopt_long(argCount, args, "hruse:l:", kLogLongOptions, NULL))
131 			!= -1) {
132 		switch (c) {
133 			case 0:
134 				break;
135 			case 'h':
136 				log_usage(0);
137 				break;
138 			case 'r':
139 				raw = true;
140 				break;
141 			case 'u':
142 				userOnly = true;
143 				break;
144 			case 's':
145 				systemOnly = true;
146 				break;
147 			case 'e':
148 				event = optarg;
149 				break;
150 			case 'l':
151 				limit = strtol(optarg, NULL, 0);
152 				break;
153 		}
154 	}
155 
156 	if (argCount - optind >= 1)
157 		job = args[optind];
158 
159 	BLaunchRoster roster;
160 	BMessage filter;
161 	if (userOnly)
162 		filter.AddBool("userOnly", true);
163 	if (systemOnly)
164 		filter.AddBool("systemOnly", true);
165 	if (event != NULL)
166 		filter.AddString("event", event);
167 	if (job != NULL)
168 		filter.AddString("job", job);
169 	if (limit != 0)
170 		filter.AddInt32("limit", limit);
171 
172 	BMessage info;
173 	status_t status = roster.GetLog(filter, info);
174 	if (status != B_OK) {
175 		fprintf(stderr, "%s: Could not get log: %s\n", kProgramName,
176 			strerror(status));
177 		exit(EXIT_FAILURE);
178 	}
179 
180 	if (raw) {
181 		info.PrintToStream();
182 		return;
183 	}
184 
185 	print_log(info);
186 
187 	BMessage user;
188 	if (info.FindMessage("user", &user) == B_OK) {
189 		if (user.HasMessage("item"))
190 			puts("User log:");
191 		print_log(user);
192 	}
193 }
194 
195 
196 static void
197 get_info(const char* name)
198 {
199 	BLaunchRoster roster;
200 	BMessage info;
201 	status_t targetStatus = roster.GetTargetInfo(name, info);
202 	if (targetStatus == B_OK) {
203 		printf("Target: %s\n", name);
204 		info.PrintToStream();
205 	}
206 
207 	info.MakeEmpty();
208 	status_t jobStatus = roster.GetJobInfo(name, info);
209 	if (jobStatus == B_OK) {
210 		printf("Job: %s\n", name);
211 		info.PrintToStream();
212 	}
213 
214 	if (jobStatus != B_OK && targetStatus != B_OK) {
215 		fprintf(stderr, "%s: Could not get target or job info for \"%s\": "
216 			"%s\n", kProgramName, name, strerror(jobStatus));
217 		exit(EXIT_FAILURE);
218 	}
219 }
220 
221 
222 static void
223 start_job(const char* name)
224 {
225 	BLaunchRoster roster;
226 	status_t status = roster.Start(name);
227 	if (status == B_NAME_NOT_FOUND)
228 		status = roster.Target(name);
229 
230 	if (status != B_OK) {
231 		fprintf(stderr, "%s: Starting job \"%s\" failed: %s\n", kProgramName,
232 			name, strerror(status));
233 		exit(EXIT_FAILURE);
234 	}
235 }
236 
237 
238 static void
239 stop_job(const char* name)
240 {
241 	BLaunchRoster roster;
242 	status_t status = roster.Stop(name);
243 	if (status == B_NAME_NOT_FOUND)
244 		status = roster.StopTarget(name);
245 
246 	if (status != B_OK) {
247 		fprintf(stderr, "%s: Stopping job \"%s\" failed: %s\n", kProgramName,
248 			name, strerror(status));
249 		exit(EXIT_FAILURE);
250 	}
251 }
252 
253 
254 static void
255 restart_job(const char* name)
256 {
257 	stop_job(name);
258 	start_job(name);
259 }
260 
261 
262 static void
263 enable_job(const char* name, bool enable)
264 {
265 	BLaunchRoster roster;
266 	status_t status = roster.SetEnabled(name, enable);
267 	if (status != B_OK) {
268 		fprintf(stderr, "%s: %s job \"%s\" failed: %s\n", kProgramName,
269 			enable ? "Enabling" : "Disabling", name, strerror(status));
270 		exit(EXIT_FAILURE);
271 	}
272 }
273 
274 
275 static void
276 usage(int status)
277 {
278 	fprintf(stderr, "Usage: %s <command>\n"
279 		"Where <command> is one of:\n"
280 		"  list - Lists all jobs (the default command)\n"
281 		"  list-targets - Lists all targets\n"
282 		"The following <command>s have a <name> argument:\n"
283 		"  start - Starts a job/target\n"
284 		"  stop - Stops a running job/target\n"
285 		"  restart - Restarts a running job/target\n"
286 		"  info - Shows info for a job/target\n",
287 		kProgramName);
288 
289 	exit(status);
290 }
291 
292 
293 int
294 main(int argc, char** argv)
295 {
296 	const char* command = "list";
297 	bool verbose = false;
298 
299 	int c;
300 	while ((c = getopt_long(argc, argv, "+hv", kLongOptions, NULL)) != -1) {
301 		switch (c) {
302 			case 0:
303 				break;
304 			case 'h':
305 				usage(0);
306 				break;
307 			case 'v':
308 				verbose = true;
309 				break;
310 			default:
311 				usage(1);
312 				break;
313 		}
314 	}
315 
316 	if (argc - optind >= 1)
317 		command = argv[optind];
318 
319 	if (strcmp(command, "list") == 0) {
320 		list_jobs(verbose);
321 	} else if (strcmp(command, "list-targets") == 0) {
322 		list_targets(verbose);
323 	} else if (strcmp(command, "log") == 0) {
324 		get_log(argc - optind, &argv[optind]);
325 	} else if (argc == optind + 1) {
326 		// For convenience (the "info" command can be omitted)
327 		get_info(command);
328 	} else {
329 		// All commands that need a name following
330 
331 		const char* name = argv[argc - 1];
332 
333 		if (strcmp(command, "info") == 0) {
334 			get_info(name);
335 		} else if (strcmp(command, "start") == 0) {
336 			start_job(name);
337 		} else if (strcmp(command, "stop") == 0) {
338 			stop_job(name);
339 		} else if (strcmp(command, "restart") == 0) {
340 			restart_job(name);
341 		} else if (strcmp(command, "enable") == 0) {
342 			enable_job(name, true);
343 		} else if (strcmp(command, "disable") == 0) {
344 			enable_job(name, false);
345 		} else {
346 			fprintf(stderr, "%s: Unknown command \"%s\".\n", kProgramName,
347 				command);
348 		}
349 	}
350 	return 0;
351 }
352