1 /* 2 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. 3 * 4 * Exacct.xs contains XS code for creating various exacct-related constants, 5 * and for providing wrappers around exacct error handling and 6 * accounting-related system calls. It also contains commonly-used utility 7 * code shared by its sub-modules. 8 */ 9 10 #include <string.h> 11 #include "exacct_common.xh" 12 13 /* 14 * Pull in the file generated by extract_defines. This contains a table 15 * of numeric constants and their string equivalents which have been extracted 16 * from the various exacct header files by the extract_defines script. 17 */ 18 #include "ExacctDefs.xi" 19 20 /* 21 * Object stash pointers - caching these speeds up the creation and 22 * typechecking of perl objects by removing the need to do a hash lookup. 23 * The peculiar variable names are so that typemaps can generate the correct 24 * package name using the typemap '$Package' variable as the root of the name. 25 */ 26 HV *Sun_Solaris_Exacct_Catalog_stash; 27 HV *Sun_Solaris_Exacct_File_stash; 28 HV *Sun_Solaris_Exacct_Object_Item_stash; 29 HV *Sun_Solaris_Exacct_Object_Group_stash; 30 HV *Sun_Solaris_Exacct_Object__Array_stash; 31 32 /* 33 * Pointer to part of the hash tree built by define_catalog_constants in 34 * Catalog.xs. This is used by catalog_id_str() when mapping from a catalog 35 * to an id string. 36 */ 37 HV *IdValueHash = NULL; 38 39 /* 40 * Last buffer size used for packing and unpacking exacct objects. 41 */ 42 static int last_bufsz = 0; 43 44 /* 45 * Common utility code. This is placed here instead of in the sub-modules to 46 * reduce the number of cross-module linker dependencies that are required, 47 * although most of the code more properly belongs in the sub-modules. 48 */ 49 50 /* 51 * This function populates the various stash pointers used by the ::Exacct 52 * module. It is called from each of the module BOOT sections to ensure the 53 * stash pointers are initialised on startup. 54 */ 55 void 56 init_stashes(void) 57 { 58 if (Sun_Solaris_Exacct_Catalog_stash == NULL) { 59 Sun_Solaris_Exacct_Catalog_stash = 60 gv_stashpv(PKGBASE "::Catalog", TRUE); 61 Sun_Solaris_Exacct_File_stash = 62 gv_stashpv(PKGBASE "::File", TRUE); 63 Sun_Solaris_Exacct_Object_Item_stash = 64 gv_stashpv(PKGBASE "::Object::Item", TRUE); 65 Sun_Solaris_Exacct_Object_Group_stash = 66 gv_stashpv(PKGBASE "::Object::Group", TRUE); 67 Sun_Solaris_Exacct_Object__Array_stash = 68 gv_stashpv(PKGBASE "::Object::_Array", TRUE); 69 } 70 } 71 72 /* 73 * This function populates the @_Constants array in the specified package 74 * based on the values extracted from the exacct header files by the 75 * extract_defines script and written to the .xi file which is included above. 76 * It also creates a const sub for each constant that returns the associcated 77 * value. It should be called from the BOOT sections of modules that export 78 * constants. 79 */ 80 #define CONST_NAME "::_Constants" 81 void 82 define_constants(const char *pkg, constval_t *cvp) 83 { 84 HV *stash; 85 char *name; 86 AV *constants; 87 88 /* Create the new perl @_Constants variable. */ 89 stash = gv_stashpv(pkg, TRUE); 90 name = New(0, name, strlen(pkg) + sizeof (CONST_NAME), char); 91 PERL_ASSERT(name != NULL); 92 strcpy(name, pkg); 93 strcat(name, CONST_NAME); 94 constants = perl_get_av(name, TRUE); 95 Safefree(name); 96 97 /* Populate @_Constants from the contents of the generated array. */ 98 for (; cvp->name != NULL; cvp++) { 99 newCONSTSUB(stash, (char *)cvp->name, newSVuv(cvp->value)); 100 av_push(constants, newSVpvn((char *)cvp->name, cvp->len)); 101 } 102 } 103 #undef CONST_NAME 104 105 /* 106 * Return a new Catalog object - only accepts an integer catalog value. 107 * Use this purely for speed when creating Catalog objects from other XS code. 108 * All other Catalog object creation should be done with the perl new() method. 109 */ 110 SV* 111 new_catalog(uint32_t cat) 112 { 113 SV *iv, *ref; 114 115 iv = newSVuv(cat); 116 ref = newRV_noinc(iv); 117 sv_bless(ref, Sun_Solaris_Exacct_Catalog_stash); 118 SvREADONLY_on(iv); 119 return (ref); 120 } 121 122 /* 123 * Return the integer catalog value from the passed Catalog or IV. 124 * Calls croak() if the SV is not of the correct type. 125 */ 126 ea_catalog_t 127 catalog_value(SV *catalog) 128 { 129 SV *sv; 130 131 /* If a reference, dereference and check it is a Catalog. */ 132 if (SvROK(catalog)) { 133 sv = SvRV(catalog); 134 if (SvIOK(sv) && 135 SvSTASH(sv) == Sun_Solaris_Exacct_Catalog_stash) { 136 return (SvIV(sv)); 137 } else { 138 croak("Parameter is not a Catalog or integer"); 139 } 140 141 /* For a plain IV, just return the value. */ 142 } else if (SvIOK(catalog)) { 143 return (SvIV(catalog)); 144 145 /* Anything else is an error */ 146 } else { 147 croak("Parameter is not a Catalog or integer"); 148 } 149 } 150 151 /* 152 * Return the string value of the id subfield of an ea_catalog_t. 153 */ 154 char * 155 catalog_id_str(ea_catalog_t catalog) 156 { 157 static ea_catalog_t cat_val = ~0U; 158 static HV *cat_hash = NULL; 159 ea_catalog_t cat; 160 ea_catalog_t id; 161 char key[12]; /* Room for dec(2^32) digits. */ 162 SV **svp; 163 164 cat = catalog & EXC_CATALOG_MASK; 165 id = catalog & EXD_DATA_MASK; 166 167 /* Fetch the correct id subhash if the catalog has changed. */ 168 if (cat_val != cat) { 169 snprintf(key, sizeof (key), "%d", cat); 170 PERL_ASSERT(IdValueHash != NULL); 171 svp = hv_fetch(IdValueHash, key, strlen(key), FALSE); 172 if (svp == NULL) { 173 cat_val = ~0U; 174 cat_hash = NULL; 175 } else { 176 HV *hv; 177 178 cat_val = cat; 179 hv = (HV *)SvRV(*svp); 180 PERL_ASSERT(hv != NULL); 181 svp = hv_fetch(hv, "value", 5, FALSE); 182 PERL_ASSERT(svp != NULL); 183 cat_hash = (HV *)SvRV(*svp); 184 PERL_ASSERT(cat_hash != NULL); 185 } 186 } 187 188 /* If we couldn't find the hash, it is a catalog we don't know about. */ 189 if (cat_hash == NULL) { 190 return ("UNKNOWN_ID"); 191 } 192 193 /* Fetch the value from the selected catalog and return it. */ 194 snprintf(key, sizeof (key), "%d", id); 195 svp = hv_fetch(cat_hash, key, strlen(key), TRUE); 196 if (svp == NULL) { 197 return ("UNKNOWN_ID"); 198 } 199 return (SvPVX(*svp)); 200 } 201 202 /* 203 * Create a new ::Object by wrapping an ea_object_t in a perl SV. This is used 204 * to wrap exacct records that have been read from a file, or packed records 205 * that have been inflated. 206 */ 207 SV * 208 new_xs_ea_object(ea_object_t *ea_obj) 209 { 210 xs_ea_object_t *xs_obj; 211 SV *sv_obj; 212 213 /* Allocate space - use perl allocator. */ 214 New(0, xs_obj, 1, xs_ea_object_t); 215 PERL_ASSERT(xs_obj != NULL); 216 xs_obj->ea_obj = ea_obj; 217 xs_obj->perl_obj = NULL; 218 sv_obj = NEWSV(0, 0); 219 PERL_ASSERT(sv_obj != NULL); 220 221 /* 222 * Initialise according to the type of the passed exacct object, 223 * and bless the perl object into the appropriate class. 224 */ 225 if (ea_obj->eo_type == EO_ITEM) { 226 if ((ea_obj->eo_catalog & EXT_TYPE_MASK) == EXT_EXACCT_OBJECT) { 227 INIT_EMBED_ITEM_FLAGS(xs_obj); 228 } else { 229 INIT_PLAIN_ITEM_FLAGS(xs_obj); 230 } 231 sv_setiv(newSVrv(sv_obj, NULL), PTR2IV(xs_obj)); 232 sv_bless(sv_obj, Sun_Solaris_Exacct_Object_Item_stash); 233 } else { 234 INIT_GROUP_FLAGS(xs_obj); 235 sv_setiv(newSVrv(sv_obj, NULL), PTR2IV(xs_obj)); 236 sv_bless(sv_obj, Sun_Solaris_Exacct_Object_Group_stash); 237 } 238 239 /* 240 * We are passing back a pointer masquerading as a perl IV, 241 * so make sure it can't be modified. 242 */ 243 SvREADONLY_on(SvRV(sv_obj)); 244 return (sv_obj); 245 } 246 247 /* 248 * Convert the perl form of an ::Object into the corresponding exacct form. 249 * This is used prior to writing an ::Object to a file, or passing it to 250 * putacct. This is only required for embedded items and groups - for normal 251 * items it is a no-op. 252 */ 253 ea_object_t * 254 deflate_xs_ea_object(SV *sv) 255 { 256 xs_ea_object_t *xs_obj; 257 ea_object_t *ea_obj; 258 259 /* Get the source xs_ea_object_t. */ 260 PERL_ASSERT(sv != NULL); 261 sv = SvRV(sv); 262 PERL_ASSERT(sv != NULL); 263 xs_obj = INT2PTR(xs_ea_object_t *, SvIV(sv)); 264 PERL_ASSERT(xs_obj != NULL); 265 ea_obj = xs_obj->ea_obj; 266 PERL_ASSERT(ea_obj != NULL); 267 268 /* Break any list this object is a part of. */ 269 ea_obj->eo_next = NULL; 270 271 /* Deal with Items containing embedded Objects. */ 272 if (IS_EMBED_ITEM(xs_obj)) { 273 xs_ea_object_t *child_xs_obj; 274 SV *perl_obj; 275 size_t bufsz; 276 277 /* Get the underlying perl object an deflate that in turn. */ 278 perl_obj = xs_obj->perl_obj; 279 PERL_ASSERT(perl_obj != NULL); 280 deflate_xs_ea_object(perl_obj); 281 perl_obj = SvRV(perl_obj); 282 PERL_ASSERT(perl_obj != NULL); 283 child_xs_obj = INT2PTR(xs_ea_object_t *, SvIV(perl_obj)); 284 PERL_ASSERT(child_xs_obj->ea_obj != NULL); 285 286 /* Free any existing object contents. */ 287 if (ea_obj->eo_item.ei_object != NULL) { 288 ea_free(ea_obj->eo_item.ei_object, 289 ea_obj->eo_item.ei_size); 290 ea_obj->eo_item.ei_object = NULL; 291 ea_obj->eo_item.ei_size = 0; 292 } 293 294 /* Pack the object. */ 295 while (1) { 296 /* Use the last buffer size as a best guess. */ 297 if (last_bufsz != 0) { 298 ea_obj->eo_item.ei_object = 299 ea_alloc(last_bufsz); 300 PERL_ASSERT(ea_obj->eo_item.ei_object != NULL); 301 } else { 302 ea_obj->eo_item.ei_object = NULL; 303 } 304 305 /* 306 * Pack the object. If the buffer is too small, 307 * we will go around again with the correct size. 308 * If unsucessful, we will bail. 309 */ 310 if ((bufsz = ea_pack_object(child_xs_obj->ea_obj, 311 ea_obj->eo_item.ei_object, last_bufsz)) == -1) { 312 ea_free(ea_obj->eo_item.ei_object, last_bufsz); 313 ea_obj->eo_item.ei_object = NULL; 314 return (NULL); 315 } else if (bufsz > last_bufsz) { 316 ea_free(ea_obj->eo_item.ei_object, last_bufsz); 317 last_bufsz = bufsz; 318 continue; 319 } else { 320 ea_obj->eo_item.ei_size = bufsz; 321 break; 322 } 323 } 324 325 /* Deal with Groups. */ 326 } else if (IS_GROUP(xs_obj)) { 327 MAGIC *mg; 328 AV *av; 329 int len, i; 330 xs_ea_object_t *ary_xs; 331 ea_object_t *ary_ea, *prev_ea; 332 333 /* Find the AV underlying the tie. */ 334 mg = mg_find(SvRV(xs_obj->perl_obj), 'P'); 335 PERL_ASSERT(mg != NULL); 336 av = (AV*)SvRV(mg->mg_obj); 337 PERL_ASSERT(av != NULL); 338 339 /* 340 * Step along the AV, deflating each object and linking it into 341 * the exacct group item list. 342 */ 343 prev_ea = ary_ea = NULL; 344 len = av_len(av) + 1; 345 ea_obj->eo_group.eg_nobjs = 0; 346 ea_obj->eo_group.eg_objs = NULL; 347 for (i = 0; i < len; i++) { 348 /* 349 * Get the source xs_ea_object_t. If the current slot 350 * in the array is empty, skip it. 351 */ 352 SV **ary_svp; 353 if ((ary_svp = av_fetch(av, i, FALSE)) == NULL) { 354 continue; 355 } 356 PERL_ASSERT(*ary_svp != NULL); 357 358 /* Deflate it. */ 359 ary_ea = deflate_xs_ea_object(*ary_svp); 360 PERL_ASSERT(ary_ea != NULL); 361 362 /* Link into the list. */ 363 ary_ea->eo_next = NULL; 364 if (ea_obj->eo_group.eg_objs == NULL) { 365 ea_obj->eo_group.eg_objs = ary_ea; 366 } 367 ea_obj->eo_group.eg_nobjs++; 368 if (prev_ea != NULL) { 369 prev_ea->eo_next = ary_ea; 370 } 371 prev_ea = ary_ea; 372 } 373 } 374 return (ea_obj); 375 } 376 377 /* 378 * Private Sun::Solaris::Exacct utility code. 379 */ 380 381 /* 382 * Return a string representation of an ea_error. 383 */ 384 static const char * 385 error_str(int eno) 386 { 387 switch (eno) { 388 case EXR_OK: 389 return ("no error"); 390 case EXR_SYSCALL_FAIL: 391 return ("system call failed"); 392 case EXR_CORRUPT_FILE: 393 return ("corrupt file"); 394 case EXR_EOF: 395 return ("end of file"); 396 case EXR_NO_CREATOR: 397 return ("no creator"); 398 case EXR_INVALID_BUF: 399 return ("invalid buffer"); 400 case EXR_NOTSUPP: 401 return ("not supported"); 402 case EXR_UNKN_VERSION: 403 return ("unknown version"); 404 case EXR_INVALID_OBJ: 405 return ("invalid object"); 406 default: 407 return ("unknown error"); 408 } 409 } 410 411 /* 412 * The XS code exported to perl is below here. Note that the XS preprocessor 413 * has its own commenting syntax, so all comments from this point on are in 414 * that form. 415 */ 416 417 MODULE = Sun::Solaris::Exacct PACKAGE = Sun::Solaris::Exacct 418 PROTOTYPES: ENABLE 419 420 # 421 # Define the stash pointers if required and create and populate @_Constants. 422 # 423 BOOT: 424 init_stashes(); 425 define_constants(PKGBASE, constants); 426 427 # 428 # Return the last exacct error as a dual-typed SV. In a numeric context the 429 # SV will evaluate to the value of an EXR_* constant, in string context to a 430 # error message. 431 # 432 SV* 433 ea_error() 434 PREINIT: 435 int eno; 436 const char *msg; 437 CODE: 438 eno = ea_error(); 439 msg = error_str(eno); 440 RETVAL = newSViv(eno); 441 sv_setpv(RETVAL, (char*) msg); 442 SvIOK_on(RETVAL); 443 OUTPUT: 444 RETVAL 445 446 # 447 # Return a string describing the last error to be encountered. If the value 448 # returned by ea_error is EXR_SYSCALL_FAIL, a string describing the value of 449 # errno will be returned. For all other values returned by ea_error() a string 450 # describing the exacct error will be returned. 451 # 452 char* 453 ea_error_str() 454 PREINIT: 455 int eno; 456 CODE: 457 eno = ea_error(); 458 if (eno == EXR_SYSCALL_FAIL) { 459 RETVAL = strerror(errno); 460 if (RETVAL == NULL) { 461 RETVAL = "unknown system error"; 462 } 463 } else { 464 RETVAL = (char*) error_str(eno); 465 } 466 OUTPUT: 467 RETVAL 468 469 # 470 # Return an accounting record for the specified task or process. idtype is 471 # either P_TASKID or P_PID and id is a process or task id. 472 # 473 SV* 474 getacct(idtype, id) 475 idtype_t idtype; 476 id_t id; 477 PREINIT: 478 int bufsz; 479 char *buf; 480 ea_object_t *ea_obj; 481 CODE: 482 /* Get the required accounting buffer. */ 483 while (1) { 484 /* Use the last buffer size as a best guess. */ 485 if (last_bufsz != 0) { 486 buf = ea_alloc(last_bufsz); 487 PERL_ASSERT(buf != NULL); 488 } else { 489 buf = NULL; 490 } 491 492 /* 493 * get the accounting record. If the buffer is too small, 494 * we will go around again with the correct size. 495 * If unsucessful, we will bail. 496 */ 497 if ((bufsz = getacct(idtype, id, buf, last_bufsz)) == -1) { 498 if (last_bufsz != 0) { 499 ea_free(buf, last_bufsz); 500 } 501 XSRETURN_UNDEF; 502 } else if (bufsz > last_bufsz) { 503 ea_free(buf, last_bufsz); 504 last_bufsz = bufsz; 505 continue; 506 } else { 507 break; 508 } 509 } 510 511 /* Unpack the buffer. */ 512 if (ea_unpack_object(&ea_obj, EUP_ALLOC, buf, bufsz) == -1) { 513 ea_free(buf, last_bufsz); 514 XSRETURN_UNDEF; 515 } 516 ea_free(buf, last_bufsz); 517 RETVAL = new_xs_ea_object(ea_obj); 518 OUTPUT: 519 RETVAL 520 521 # 522 # Write an accounting record into the system accounting file. idtype is 523 # either P_TASKID or P_PID and id is a process or task id. value may be either 524 # an ::Exacct::Object, in which case it will be packed and inserted in the 525 # file, or a SV which will be converted to a string and inserted into the file. 526 # 527 SV* 528 putacct(idtype, id, value) 529 idtype_t idtype; 530 id_t id; 531 SV *value; 532 PREINIT: 533 HV *stash; 534 STRLEN bufsz; 535 int flags, ret; 536 char *buf; 537 CODE: 538 /* If it is an ::Object::Item or ::Object::Group, pack it. */ 539 stash = SvROK(value) ? SvSTASH(SvRV(value)) : NULL; 540 if (stash == Sun_Solaris_Exacct_Object_Item_stash || 541 stash == Sun_Solaris_Exacct_Object_Group_stash) { 542 ea_object_t *obj; 543 544 /* Deflate the object. */ 545 if ((obj = deflate_xs_ea_object(value)) == NULL) { 546 XSRETURN_NO; 547 } 548 549 /* Pack the object. */ 550 while (1) { 551 /* Use the last buffer size as a best guess. */ 552 if (last_bufsz != 0) { 553 buf = ea_alloc(last_bufsz); 554 PERL_ASSERT(buf != NULL); 555 } else { 556 buf = NULL; 557 } 558 559 /* 560 * Pack the object. If the buffer is too small, we 561 * will go around again with the correct size. 562 * If unsucessful, we will bail. 563 */ 564 if ((bufsz = ea_pack_object(obj, buf, last_bufsz)) 565 == -1) { 566 if (last_bufsz != 0) { 567 ea_free(buf, last_bufsz); 568 } 569 XSRETURN_NO; 570 } else if (bufsz > last_bufsz) { 571 ea_free(buf, last_bufsz); 572 last_bufsz = bufsz; 573 continue; 574 } else { 575 break; 576 } 577 } 578 flags = EP_EXACCT_OBJECT; 579 580 /* Otherwise treat it as normal SV - convert to a string. */ 581 } else { 582 buf = SvPV(value, bufsz); 583 flags = EP_RAW; 584 } 585 586 /* Call putacct to write the buffer */ 587 RETVAL = putacct(idtype, id, buf, bufsz, flags) == 0 588 ? &PL_sv_yes : &PL_sv_no; 589 590 /* Clean up if we allocated a buffer. */ 591 if (flags == EP_EXACCT_OBJECT) { 592 ea_free(buf, last_bufsz); 593 } 594 OUTPUT: 595 RETVAL 596 597 # 598 # Write an accounting record for the specified task or process. idtype is 599 # either P_TASKID or P_PID, id is a process or task id and flags is either 600 # EW_PARTIAL or EW_INTERVAL. 601 # 602 int 603 wracct(idtype, id, flags) 604 idtype_t idtype; 605 id_t id; 606 int flags;