1/* 2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <assert.h> 27#include <ctype.h> 28#include <pwd.h> 29#include "options.h" 30#include "preferences.h" 31 32#include "log.h" 33 34enum { 35 OPT_COUNTING = 1, 36 OPT_FRAMEWORK_OFF, 37 OPT_FRAMEWORK_ON, 38 OPT_HELP, 39 OPT_ORDER, 40 OPT_SECONDARY_ORDER, 41 OPT_SLEEP, 42 OPT_INTERVAL, 43 OPT_SAMPLES, 44 OPT_NCOLS, 45 OPT_NPROCS, 46 OPT_STATS, 47 OPT_PID, 48 OPT_USER, 49 OPT_DEBUGLOG, 50 OPT_U_SORT, 51 OPT_SWAP, 52 OPT_MMR_OFF, 53 OPT_MMR_ON, 54 /*compat/deprecated options*/ 55 OPT_ACCUM, 56 OPT_DELTA, 57 OPT_ABSOLUTE 58}; 59 60enum { 61 TOP_OPTION_REQUIRED = 1, /* A -flag value pair is specified as required. */ 62 TOP_OPTION_SET /* The option is a boolean to enable/disable something. */ 63}; 64 65 66struct top_option { 67 const char *option_string; 68 int option_flag; 69 int option_value; 70}; 71 72/* Return true if found, and false if not. */ 73/* The caller of this is expected to have initialized the *offset value to a valid argv array offset. */ 74bool top_option_get(int argc, char *argv[], struct top_option *opts, int *offset, int *optvalue, char **argresult) { 75 int opti; 76 77 assert(*offset < argc); 78 79 /* Set the argument result (AKA the option value) to NULL. */ 80 *argresult = NULL; 81 *optvalue = -1; 82 83 for(opti = 0; opts[opti].option_string; ++opti) { 84 if(TOP_OPTION_REQUIRED == opts[opti].option_flag) { 85 /* First check for an exact match. */ 86 /* Otherwise check for a single char option with a value like -n4. */ 87 if(!strcmp(argv[*offset], opts[opti].option_string)) { 88 if((*offset + 1) >= argc) { 89 /* The option was something like -stats without a value. */ 90 return false; 91 } 92 *argresult = argv[*offset + 1]; 93 *optvalue = opts[opti].option_value; 94 *offset += 2; 95 return true; 96 } else if(2 == strlen(opts[opti].option_string) && strlen(argv[*offset]) >= 2 97 && opts[opti].option_string[0] == argv[*offset][0] 98 && opts[opti].option_string[1] == argv[*offset][1]) { 99 /* We found a pattern like -n123 and the argresult should be 123. */ 100 *argresult = argv[*offset] + 2; 101 *optvalue = opts[opti].option_value; 102 *offset += 1; 103 return true; 104 } 105 } else { 106 /* TOP_OPTION_SET */ 107 if(!strcmp(argv[*offset], opts[opti].option_string)) { 108 *optvalue = opts[opti].option_value; 109 *offset += 1; 110 return true; 111 } 112 } 113 } 114 115 return false; 116} 117 118/* Note: it's important that options with the same prefix have the long option in this struct first. */ 119static struct top_option opts[] = { 120 {"-stats", TOP_OPTION_REQUIRED, OPT_STATS}, 121 {"-ncols", TOP_OPTION_REQUIRED, OPT_NCOLS}, 122 {"-pid", TOP_OPTION_REQUIRED, OPT_PID}, 123 {"-user", TOP_OPTION_REQUIRED, OPT_USER}, 124 {"-c", TOP_OPTION_REQUIRED, OPT_COUNTING}, 125 {"-F", TOP_OPTION_SET, OPT_FRAMEWORK_OFF}, 126 {"-f", TOP_OPTION_SET, OPT_FRAMEWORK_ON}, 127 {"-h", TOP_OPTION_SET, OPT_HELP}, 128 {"-o", TOP_OPTION_REQUIRED, OPT_ORDER}, 129 {"-O", TOP_OPTION_REQUIRED, OPT_SECONDARY_ORDER}, 130 {"-s", TOP_OPTION_REQUIRED, OPT_SLEEP}, 131 {"-i", TOP_OPTION_REQUIRED, OPT_INTERVAL}, 132 {"-l", TOP_OPTION_REQUIRED, OPT_SAMPLES}, 133 {"-n", TOP_OPTION_REQUIRED, OPT_NPROCS}, 134 {"-U", TOP_OPTION_REQUIRED, OPT_USER}, 135 {"-u", TOP_OPTION_SET, OPT_U_SORT}, 136 {"-S", TOP_OPTION_SET, OPT_SWAP}, 137 {"-R", TOP_OPTION_SET, OPT_MMR_OFF}, 138 {"-r", TOP_OPTION_SET, OPT_MMR_ON}, 139 /*compat/deprecated options*/ 140 {"-a", TOP_OPTION_SET, OPT_ACCUM}, 141 {"-d", TOP_OPTION_SET, OPT_DELTA}, 142 {"-e", TOP_OPTION_SET, OPT_ABSOLUTE}, 143 {NULL, 0, 0} 144}; 145 146void top_options_init(void) { 147 /* do nothing */ 148} 149 150void top_options_usage(FILE *fp, char *argv0) { 151 fprintf(fp, 152 "%s usage: %s\n" 153 "\t\t[-a | -d | -e | -c <mode>]\n" 154 "\t\t[-F | -f]\n" 155 "\t\t[-h]\n" 156 "\t\t[-i <interval>]\n" 157 "\t\t[-l <samples>]\n" 158 "\t\t[-ncols <columns>]\n" 159 "\t\t[-o <key>] [-O <secondaryKey>]\n" 160 "\t\t[-R | -r]\n" 161 "\t\t[-S]\n" 162 "\t\t[-s <delay>]\n" 163 "\t\t[-n <nprocs>]\n" 164 "\t\t[-stats <key(s)>]\n" 165 "\t\t[-pid <processid>]\n" 166 "\t\t[-user <username>]\n" 167 "\t\t[-U <username>]\n" 168 "\t\t[-u]\n", 169 argv0, argv0); 170 171 fprintf(fp, "\n"); 172} 173 174static bool string_is_integer(const char *s) { 175 const char *b = s; 176 bool indicator = false; 177 178 /*skip whitespace*/ 179 for(; *s && isspace((int)*s); ++s) 180 /*empty body*/; 181 182 if('-' == *s || '+' == *s) { 183 ++s; 184 indicator = true; 185 } 186 187 for(; *s && isdigit((int)*s); ++s) 188 /*empty body*/; 189 190 /* 191 * At this point we could have trailing whitespace, but 192 * in the use case that is not a real problem. If this is 193 * reused we might want the whitespace handled here. 194 */ 195 196 if('\0' == *s && b != s) { 197 /* Check if it was just - or + */ 198 if(indicator) { 199 if((s - b) > 1) { 200 return true; 201 } else { 202 return false; 203 } 204 } else { 205 return true; 206 } 207 } 208 209 return false; 210} 211 212/* Return true if an error occurred. */ 213bool top_options_parse(int argc, char *argv[]) { 214 int offset = 1; 215 216 while(offset < argc) { 217 char *optarg; 218 int optvalue; 219 220 if(false == top_option_get(argc, argv, opts, &offset, &optvalue, &optarg)) { 221 fprintf(stderr, "invalid option or syntax: %s\n", argv[offset]); 222 return true; 223 } 224 225 switch(optvalue) { 226 case OPT_COUNTING: 227 if(top_prefs_set_mode(optarg)) { 228 fprintf(stderr, "invalid argument for -c: %s\n", optarg); 229 return true; 230 } 231 break; 232 233 case OPT_FRAMEWORK_OFF: 234 top_prefs_set_frameworks(false); 235 break; 236 237 case OPT_FRAMEWORK_ON: 238 top_prefs_set_frameworks(true); 239 break; 240 241 case OPT_HELP: 242 top_options_usage(stdout, argv[0]); 243 exit(EXIT_SUCCESS); 244 break; 245 246 case OPT_INTERVAL: { 247 int n; 248 249 if(!string_is_integer(optarg)) { 250 fprintf(stderr, 251 "invalid interval number (not an integer): %s\n", 252 optarg); 253 return true; 254 } 255 256 n = atoi(optarg); 257 258 if(n < 1) { 259 fprintf(stderr, "invalid argument for -i: %s\n", optarg); 260 return true; 261 } 262 263 top_prefs_set_frameworks_interval(n); 264 } 265 break; 266 267 case OPT_SAMPLES: { 268 int s; 269 270 if(!string_is_integer(optarg)) { 271 fprintf(stderr, 272 "invalid number of samples (not an integer): %s\n", 273 optarg); 274 return true; 275 } 276 277 s = atoi(optarg); 278 279 if(s < 0) { 280 fprintf(stderr, "invalid number of samples: %d\n", s); 281 return true; 282 } 283 284 top_prefs_set_samples(s); 285 } 286 break; 287 288 case OPT_NCOLS: { 289 int n; 290 291 if(!string_is_integer(optarg)) { 292 fprintf(stderr, 293 "invalid argument for -ncols (not an integer): %s\n", 294 optarg); 295 return true; 296 } 297 298 n = atoi(optarg); 299 300 if(n < 1) { 301 fprintf(stderr, "-ncols requires an argument > 0\n"); 302 return true; 303 } 304 305 top_prefs_set_ncols(n); 306 } 307 break; 308 309 case OPT_NPROCS: { 310 int n; 311 312 if(!string_is_integer(optarg)) { 313 fprintf(stderr, 314 "invalid argument for -n (not an integer): %s\n", 315 optarg); 316 return true; 317 } 318 319 n = atoi(optarg); 320 321 if(n < 0) { 322 fprintf(stderr, "-n argument may not be negative: %s\n", 323 optarg); 324 return true; 325 } 326 327 top_prefs_set_nprocs(n); 328 } 329 break; 330 331 case OPT_ORDER: 332 if(top_prefs_set_sort(optarg)) { 333 fprintf(stderr, "invalid argument -o: %s\n", optarg); 334 return true; 335 } 336 break; 337 338 case OPT_SECONDARY_ORDER: 339 if(top_prefs_set_secondary_sort(optarg)) { 340 fprintf(stderr, "invalid argument -O: %s\n", optarg); 341 return true; 342 } 343 break; 344 345 case OPT_SLEEP: { 346 int s; 347 348 if(!string_is_integer(optarg)) { 349 fprintf(stderr, 350 "invalid argument for sleep interval (not an integer):" 351 " %s\n", 352 optarg); 353 return true; 354 } 355 356 s = atoi(optarg); 357 358 if(s < 0) { 359 fprintf(stderr, "invalid argument for -s: %s\n", optarg); 360 return true; 361 } 362 363 top_prefs_set_sleep(s); 364 } 365 break; 366 367 case OPT_STATS: 368 if(top_prefs_set_stats(optarg)) { 369 fprintf(stderr, "invalid argument for -stats: %s\n", optarg); 370 return true; 371 } 372 break; 373 374 case OPT_PID: { 375 pid_t p; 376 377 if(!string_is_integer(optarg)) { 378 fprintf(stderr, 379 "invalid -pid argument (not an integer): %s\n", optarg); 380 return true; 381 } 382 383 p = atoi(optarg); 384 385 if(p < 0) { 386 fprintf(stderr, "pid arguments can not be < 0: %s\n", 387 optarg); 388 return true; 389 } 390 391 top_prefs_set_pid(p); 392 } 393 break; 394 395 case OPT_USER: { 396 struct passwd *pw; 397 398 pw = getpwnam(optarg); 399 400 if(NULL == pw) { 401 endpwent(); 402 fprintf(stderr, "invalid user: %s\n", optarg); 403 return true; 404 } 405 406 top_prefs_set_user(optarg); 407 top_prefs_set_user_uid(pw->pw_uid); 408 409 endpwent(); 410 } 411 break; 412 413 case OPT_U_SORT: { 414 if(top_prefs_set_sort("cpu") 415 || top_prefs_set_secondary_sort("time")) { 416 fprintf(stderr, 417 "An unexpected error occurred while performing the -u " 418 "preference setting.\n"); 419 abort(); 420 } 421 } 422 break; 423 424 case OPT_MMR_OFF: 425 top_prefs_set_mmr(false); 426 break; 427 428 case OPT_MMR_ON: 429 top_prefs_set_mmr(true); 430 break; 431 432 case OPT_SWAP: 433 top_prefs_set_swap(true); 434 break; 435 436 /*compat/deprecated options*/ 437 case OPT_ACCUM: 438 if(top_prefs_set_mode("a")) { 439 fprintf(stderr, 440 "An internal top error has occurred unexpectedly!\n"); 441 abort(); 442 } 443 break; 444 445 case OPT_DELTA: 446 if(top_prefs_set_mode("d")) { 447 fprintf(stderr, 448 "An internal top error has occurred unexpectedly!\n"); 449 abort(); 450 } 451 break; 452 453 case OPT_ABSOLUTE: 454 if(top_prefs_set_mode("e")) { 455 fprintf(stderr, 456 "An internal top error has occurred unexpectedly!\n"); 457 abort(); 458 } 459 break; 460 } /*end switch*/ 461 } 462 463 return false; 464} 465