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