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
init_stashes(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
define_constants(const char * pkg,constval_t * cvp)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*
new_catalog(uint32_t cat)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
catalog_value(SV * catalog)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 *
catalog_id_str(ea_catalog_t catalog)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 *
new_xs_ea_object(ea_object_t * ea_obj)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 *
deflate_xs_ea_object(SV * sv)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 *
error_str(int eno)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 unsigned int 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;
607