1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "@(#)usage_tracking.c 1.8 09/05/26 SMI" 28 29 #include <sys/types.h> 30 #include <sys/systeminfo.h> 31 #include <sys/stat.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <strings.h> 37 #include <string.h> 38 #include <time.h> 39 #include <limits.h> 40 #include <signal.h> 41 42 #include <usage_tracking/usage_tracking.h> 43 44 #ifdef ASSERT_USAGE_TRACKING 45 #include <diskomizer/assert.h> 46 #else 47 #define assert(A) 48 #endif 49 50 #define SYSINFO_BUF_SIZE 25 51 52 struct key_list { 53 char *key; 54 struct key_list *next; 55 }; 56 57 typedef struct usage_track { 58 struct key_list *list; 59 char *sendmail; 60 char *to; 61 char *from; 62 char *tool; 63 char *version; 64 char *dom; 65 char *str; 66 int len; 67 int indent; 68 int errors; 69 } usage_track_t; 70 71 static const char xml_version[] = "<?xml version=\"1.0\"?>\n"; 72 73 #define NOT_NULL_FREE(X) if (X != NULL) { free(X); X = NULL; } 74 75 static struct key_list * 76 push_key(usage_track_t *usage_trackp, const char *key) 77 { 78 struct key_list *x; 79 80 if ((x = malloc(sizeof (struct key_list))) == NULL) { 81 return (NULL); 82 } 83 if ((x->key = strdup(key)) == NULL) { 84 free(x); 85 return (NULL); 86 } 87 x->next = usage_trackp->list; 88 usage_trackp->list = x; 89 return (x); 90 } 91 92 static char * 93 pop_key(usage_track_t *usage_trackp) 94 { 95 char *x; 96 struct key_list *l; 97 98 if ((l = usage_trackp->list) == NULL) { 99 return (NULL); 100 } 101 x = l->key; 102 usage_trackp->list = l->next; 103 free(l); 104 105 return (x); 106 } 107 108 static void 109 free_usage_track(usage_track_t *usage_trackp) 110 { 111 char *key; 112 113 NOT_NULL_FREE(usage_trackp->to); 114 NOT_NULL_FREE(usage_trackp->from); 115 NOT_NULL_FREE(usage_trackp->sendmail); 116 NOT_NULL_FREE(usage_trackp->dom); 117 NOT_NULL_FREE(usage_trackp->str); 118 NOT_NULL_FREE(usage_trackp->tool); 119 NOT_NULL_FREE(usage_trackp->version); 120 121 while ((key = pop_key(usage_trackp)) != NULL) { 122 free(key); 123 } 124 free(usage_trackp); 125 } 126 127 128 static void 129 close_open_keys(usage_track_t *usage_trackp) 130 { 131 while (usage_trackp->list != NULL) { 132 usage_tracking_close_key((void *)usage_trackp); 133 } 134 } 135 136 static void 137 indent(usage_track_t *usage_trackp) 138 { 139 int i; 140 141 for (i = 0; i < usage_trackp->indent; i++) { 142 usage_trackp->str[usage_trackp->len++] = '\t'; 143 } 144 } 145 146 void 147 usage_tracking_store_key_value(void *handle, 148 const char *key, const char *value) 149 { 150 /* 151 * No need to push and pop the key as we are just doing the whole 152 * thing in one hit. 153 */ 154 int this_len; 155 char *tmp; 156 usage_track_t *usage_trackp = handle; 157 158 if (usage_trackp == NULL) { 159 return; 160 } 161 162 this_len = (3 * usage_trackp->indent) + (2 * strlen(key)) + 163 strlen(value) + 9; 164 165 if ((tmp = realloc(usage_trackp->str, 166 usage_trackp->len + this_len + 1)) == NULL) { 167 usage_trackp->errors++; 168 } else { 169 int x = usage_trackp->len; 170 171 usage_trackp->str = tmp; 172 indent(usage_trackp); 173 usage_trackp->len += 174 sprintf(&usage_trackp->str[usage_trackp->len], 175 "<%s>\n", key); 176 177 indent(usage_trackp); 178 179 usage_trackp->len += 180 sprintf(&usage_trackp->str[usage_trackp->len], 181 "\t%s\n", value); 182 indent(usage_trackp); 183 184 usage_trackp->len += 185 sprintf(&usage_trackp->str[usage_trackp->len], 186 "</%s>\n", key); 187 188 assert(x + this_len == usage_trackp->len); 189 } 190 } 191 192 void 193 usage_tracking_store_key_value_int(void *handle, 194 const char *key, const long long value) 195 { 196 char buf[64]; 197 sprintf(buf, "%lld", value); 198 usage_tracking_store_key_value(handle, key, buf); 199 } 200 201 int 202 usage_tracking_open_key(void *handle, 203 const char *key, const char *version, const char *id) 204 { 205 int this_len; 206 char *tmp; 207 usage_track_t *usage_trackp = handle; 208 209 if (usage_trackp == NULL) { 210 return (0); 211 } 212 213 this_len = strlen(key) + /* "<>\n" */ 3 + usage_trackp->indent; 214 215 if (version != NULL) { 216 this_len += strlen(version) + /* " version=\"\"" */ 11; 217 } 218 219 if (id != NULL) { 220 this_len += strlen(id) + /* " id=\"\"" */ 6; 221 } 222 223 if ((tmp = realloc(usage_trackp->str, 224 usage_trackp->len + this_len + 1)) == NULL) { 225 usage_trackp->errors++; 226 return (0); 227 } else { 228 int x = usage_trackp->len; 229 int i; 230 231 if (push_key(usage_trackp, key) == NULL) { 232 usage_trackp->errors++; 233 return (0); 234 } 235 236 usage_trackp->str = tmp; 237 238 indent(usage_trackp); 239 240 i = sprintf(&usage_trackp->str[usage_trackp->len], "<%s", key); 241 usage_trackp->len += i; 242 243 if (version != NULL) { 244 i = sprintf(&usage_trackp->str[usage_trackp->len], 245 " version=\"%s\"", version); 246 usage_trackp->len += i; 247 } 248 249 if (id != NULL) { 250 i = sprintf(&usage_trackp->str[usage_trackp->len], 251 " id=\"%s\"", id); 252 usage_trackp->len += i; 253 } 254 255 i = sprintf(&usage_trackp->str[usage_trackp->len], ">\n"); 256 usage_trackp->len += i; 257 258 usage_trackp->indent++; 259 260 assert(x + this_len == usage_trackp->len); 261 } 262 return (1); 263 } 264 265 int 266 usage_tracking_close_key(void *handle) 267 { 268 int this_len; 269 int ret; 270 char *key; 271 char *tmp; 272 usage_track_t *usage_trackp = handle; 273 274 if (usage_trackp == NULL) { 275 return (0); 276 } 277 278 if ((key = pop_key(usage_trackp)) == NULL || 279 --usage_trackp->indent < 0) { 280 usage_trackp->indent = 0; 281 usage_trackp->errors++; 282 usage_tracking_store_key_value(handle, "usage_track_error", 283 "Unbalanced close"); 284 return (0); 285 } 286 this_len = strlen(key) + /* "</>\n" */ 4 + usage_trackp->indent; 287 288 if ((tmp = realloc(usage_trackp->str, 289 usage_trackp->len + this_len + 1)) == NULL) { 290 usage_trackp->errors++; 291 ret = 0; 292 } else { 293 int x = usage_trackp->len; 294 295 usage_trackp->str = tmp; 296 297 indent(usage_trackp); 298 299 sprintf(&usage_trackp->str[usage_trackp->len], 300 "</%s>\n", key); 301 usage_trackp->len += strlen(key) + 4; 302 assert(x + this_len == usage_trackp->len); 303 ret = 1; 304 } 305 free(key); 306 return (ret); 307 } 308 309 void 310 usage_tracking_store_value(void *handle, 311 const char *value) 312 { 313 int this_len; 314 char *tmp; 315 usage_track_t *usage_trackp = handle; 316 317 if (usage_trackp == NULL) { 318 return; 319 } 320 321 this_len = strlen(value) + /* "\n" */ 1 + usage_trackp->indent; 322 323 if ((tmp = realloc(usage_trackp->str, 324 usage_trackp->len + this_len + 1)) == NULL) { 325 usage_trackp->errors++; 326 return; 327 } else { 328 int x = usage_trackp->len; 329 usage_trackp->str = tmp; 330 331 indent(handle); 332 333 usage_trackp->len += 334 sprintf(&usage_trackp->str[usage_trackp->len], 335 "%s\n", value); 336 assert(x + this_len == usage_trackp->len); 337 } 338 } 339 340 static int 341 usage_tracking_ok(const char *dom, const char *host) 342 { 343 char *buf; 344 char *tmp; 345 int dom_len = strlen(dom); 346 long len = 0; 347 long ret; 348 char x[1]; 349 350 if (dom_len == 0 || (ret = sysinfo(SI_SRPC_DOMAIN, x, 1)) <= dom_len) { 351 return (0); 352 } 353 if ((buf = malloc(ret)) == NULL) { 354 return (0); 355 } 356 len = ret; 357 358 if ((ret = sysinfo(SI_SRPC_DOMAIN, buf, len)) > len) { 359 free(buf); 360 return (0); 361 } 362 363 ret--; /* remove the space for the NULL from the length */ 364 365 tmp = &buf[ret - dom_len]; 366 367 if (strcasecmp(tmp, dom) != 0) { 368 free(buf); 369 return (0); 370 } 371 free(buf); 372 return (1); 373 } 374 375 static char * 376 now_str(char *str) 377 { 378 time_t now = time(NULL); 379 380 cftime(str, "%D %T %z", &now); 381 return (str); 382 } 383 384 static void 385 usage_tracking_start_time(usage_track_t *usage_trackp) 386 { 387 if (usage_tracking_open_key(usage_trackp, "START_TIME", NULL, NULL)) { 388 time_t now = time(NULL); 389 char buf[128]; 390 int len; 391 392 ctime_r(&now, buf, sizeof (buf)); 393 394 len = strlen(buf) - 1; 395 396 if (buf[len] = '\n') { 397 buf[len] = NULL; 398 } 399 400 usage_tracking_store_key_value(usage_trackp, 401 "LOCAL_TIME", buf); 402 usage_tracking_store_key_value_int(usage_trackp, 403 "UNIX_TIME", now); 404 405 usage_tracking_close_key((void *)usage_trackp); 406 } 407 } 408 409 static void 410 usage_tracking_sysinfo(usage_track_t *usage_trackp, 411 int command, const char *str) 412 { 413 char buf[SYSINFO_BUF_SIZE]; 414 char *t; 415 int x; 416 417 if ((x = sysinfo(command, buf, sizeof (buf))) > sizeof (buf)) { 418 if ((t = malloc(x)) != NULL) { 419 (void) sysinfo(command, t, x); 420 } else { 421 t = buf; 422 } 423 } else { 424 t = buf; 425 } 426 427 usage_tracking_store_key_value(usage_trackp, str, t); 428 429 if (t != buf) { 430 free(t); 431 } 432 433 } 434 435 436 static void 437 usage_tracking_system_info(usage_track_t *usage_trackp) 438 { 439 if (usage_tracking_open_key(usage_trackp, "SYSTEM_INFO", 440 NULL, NULL)) { 441 442 usage_tracking_sysinfo(usage_trackp, SI_SYSNAME, "OSNAME"); 443 444 usage_tracking_sysinfo(usage_trackp, SI_HOSTNAME, "NODENAME"); 445 446 usage_tracking_sysinfo(usage_trackp, SI_SRPC_DOMAIN, 447 "DOMAINNAME"); 448 449 usage_tracking_sysinfo(usage_trackp, SI_RELEASE, "OSVERSION"); 450 451 usage_tracking_sysinfo(usage_trackp, SI_MACHINE, "MACHINE"); 452 453 usage_tracking_sysinfo(usage_trackp, SI_PLATFORM, "PLATFORM"); 454 455 usage_tracking_sysinfo(usage_trackp, SI_HW_SERIAL, "SERIAL"); 456 457 usage_tracking_sysinfo(usage_trackp, SI_HW_PROVIDER, 458 "HWPROVIDER"); 459 460 usage_tracking_sysinfo(usage_trackp, SI_ISALIST, 461 "ISALIST"); 462 463 usage_tracking_close_key((void *)usage_trackp); 464 } 465 } 466 467 static void 468 usage_tracking_tool_info(usage_track_t *usage_trackp) 469 { 470 if (usage_tracking_open_key(usage_trackp, "TOOL_INFO", NULL, NULL)) { 471 usage_tracking_store_key_value(usage_trackp, "NAME", 472 usage_trackp->tool); 473 usage_tracking_store_key_value(usage_trackp, "VERSION", 474 usage_trackp->version); 475 usage_tracking_store_key_value_int(usage_trackp, 476 "POINTER_BITS", sizeof (void *) * CHAR_BIT); 477 478 usage_tracking_close_key((void *)usage_trackp); 479 } 480 } 481 482 483 static void 484 usage_tracking_general(usage_track_t *usage_trackp) 485 { 486 if (usage_tracking_open_key(usage_trackp, "GENERAL", NULL, NULL)) { 487 usage_tracking_start_time(usage_trackp); 488 usage_tracking_system_info(usage_trackp); 489 usage_tracking_tool_info(usage_trackp); 490 491 usage_tracking_close_key((void *)usage_trackp); 492 } 493 } 494 495 void * 496 open_usage_tracking(const char *dom, 497 const char *sendmail, 498 const char *to, 499 const char *from, 500 const char *tool, 501 const char *version) 502 { 503 usage_track_t *usage_trackp; 504 505 if ((usage_trackp = malloc(sizeof (usage_track_t))) == NULL) { 506 return (NULL); 507 } 508 509 memset(usage_trackp, 0, sizeof (usage_track_t)); 510 511 if ((usage_trackp->dom = strdup(dom)) == NULL || 512 (usage_trackp->sendmail = strdup(sendmail)) == NULL || 513 (usage_trackp->from = strdup(from)) == NULL || 514 (usage_trackp->to = strdup(to)) == NULL || 515 (usage_trackp->tool = strdup(tool)) == NULL || 516 (usage_trackp->version = strdup(version)) == NULL) { 517 free_usage_track(usage_trackp); 518 return (NULL); 519 } 520 521 if ((usage_trackp->str = strdup(xml_version)) != NULL) { 522 usage_trackp->len = sizeof (xml_version) - 1; 523 } 524 525 if (usage_tracking_open_key(usage_trackp, 526 USAGE_TRACKING, USAGE_TRACKING_VERSION, tool)) { 527 528 usage_tracking_general(usage_trackp); 529 530 usage_tracking_open_key( 531 usage_trackp, "TOOLDEFINED", NULL, NULL); 532 } 533 534 return (usage_trackp); 535 } 536 537 void 538 close_usage_tracking(void *handle) 539 { 540 usage_track_t *usage_trackp = handle; 541 542 if (usage_trackp == NULL) { 543 return; 544 } 545 546 free_usage_track(usage_trackp); 547 } 548 549 550 void 551 send_usage_tracking(void *handle) 552 { 553 FILE *fd; 554 char *command; 555 char now[256]; 556 sigset_t nset; /* new set */ 557 sigset_t oset; /* old set */ 558 int sigprocmask_status; 559 usage_track_t *usage_trackp = handle; 560 561 if (usage_trackp == NULL) { 562 return; 563 } 564 565 now_str(now); 566 567 if (!usage_tracking_ok(usage_trackp->dom, NULL)) { 568 return; 569 } 570 571 close_open_keys(usage_trackp); 572 573 if ((command = malloc(strlen(usage_trackp->sendmail) + 574 strlen(usage_trackp->to) + 2)) == NULL) { 575 return; 576 } 577 578 sprintf(command, "%s %s", usage_trackp->sendmail, usage_trackp->to); 579 580 if (access(usage_trackp->sendmail, X_OK) != 0) { 581 free(command); 582 return; 583 } 584 585 fd = popen(command, "w"); 586 587 free(command); 588 589 if (fd == NULL) { 590 return; 591 } 592 593 sigaddset(&nset, SIGPIPE); 594 sigprocmask_status = sigprocmask(SIG_BLOCK, &nset, &oset); 595 596 fprintf(fd, "From: %s\nTo: %s\nDate: %s\nSubject: %s\n%s\n.\n", 597 usage_trackp->from, 598 usage_trackp->to, now, 599 usage_trackp->tool, usage_trackp->str); 600 601 pclose(fd); 602 if (sigprocmask_status == 0) { 603 (void) sigprocmask(SIG_SETMASK, &nset, &oset); 604 } 605 } 606 607 void 608 save_usage_tracking(void *handle, const char *file) 609 { 610 int fd; 611 int i; 612 int written; 613 usage_track_t *usage_trackp = handle; 614 615 if (usage_trackp == NULL) { 616 return; 617 } 618 619 if (file == NULL || 620 (fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) { 621 return; 622 } 623 624 close_open_keys(usage_trackp); 625 626 for (written = 0; written < usage_trackp->len; written += i) { 627 i = write(fd, &usage_trackp->str[written], 628 usage_trackp->len - written); 629 if (i == -1) { 630 break; 631 } 632 } 633 close(fd); 634 } 635