1 /* 2 * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2014 Racktop Systems. 4 */ 5 /* 6 * Project.xs contains XS wrappers for the project database maniplulation 7 * functions as provided by libproject and described in getprojent(3EXACCT). 8 */ 9 10 /* Solaris includes. */ 11 #include <zone.h> 12 #include <project.h> 13 #include <pool.h> 14 #include <sys/pool_impl.h> 15 #include <rctl.h> 16 #include <stdio.h> 17 18 /* Perl includes. */ 19 #include "EXTERN.h" 20 #if __GNUC__ >= 5 21 #include "perl.h" 22 #else 23 #define _Thread_local 24 #include "perl.h" 25 #undef _Thread_local 26 #undef PERL_GET_CONTEXT 27 #undef PERL_SET_CONTEXT 28 #define PERL_GET_CONTEXT PTHREAD_GETSPECIFIC(PL_thr_key) 29 #define PERL_SET_CONTEXT(t) Perl_set_context((void*)t) 30 #endif 31 #include "XSUB.h" 32 33 /* 34 * Convert and save a struct project on the perl XS return stack. 35 * In a void context it returns nothing, in a scalar context it returns just 36 * the name of the project and in a list context it returns a 6-element list 37 * consisting of (name, projid, comment, users, groups, attr), where users and 38 * groups are references to arrays containing the appropriate lists. 39 */ 40 static int 41 pushret_project(const struct project *proj) 42 { 43 char **cp; 44 AV *ary; 45 46 dSP; 47 if (GIMME_V == G_SCALAR) { 48 EXTEND(SP, 1); 49 PUSHs(sv_2mortal(newSVpv(proj->pj_name, 0))); 50 PUTBACK; 51 return (1); 52 } else if (GIMME_V == G_ARRAY) { 53 EXTEND(SP, 6); 54 PUSHs(sv_2mortal(newSVpv(proj->pj_name, 0))); 55 PUSHs(sv_2mortal(newSViv(proj->pj_projid))); 56 PUSHs(sv_2mortal(newSVpv(proj->pj_comment, 0))); 57 ary = newAV(); 58 for (cp = proj->pj_users; *cp != NULL; cp++) { 59 av_push(ary, newSVpv(*cp, 0)); 60 } 61 PUSHs(sv_2mortal(newRV_noinc((SV *)ary))); 62 ary = newAV(); 63 for (cp = proj->pj_groups; *cp != NULL; cp++) { 64 av_push(ary, newSVpv(*cp, 0)); 65 } 66 PUSHs(sv_2mortal(newRV_noinc((SV *)ary))); 67 PUSHs(sv_2mortal(newSVpv(proj->pj_attr, 0))); 68 PUTBACK; 69 return (6); 70 } else { 71 return (0); 72 } 73 } 74 75 static int 76 pwalk_cb(const projid_t project, void *walk_data) 77 { 78 int *nitemsp; 79 80 dSP; 81 nitemsp = (int *) walk_data; 82 EXTEND(SP, 1); 83 PUSHs(sv_2mortal(newSViv(project))); 84 (*nitemsp)++; 85 PUTBACK; 86 return (0); 87 } 88 89 /* 90 * The XS code exported to perl is below here. Note that the XS preprocessor 91 * has its own commenting syntax, so all comments from this point on are in 92 * that form. Note also that the PUTBACK; lines are necessary to synchronise 93 * the local and global views of the perl stack before calling pushret_project, 94 * as the code generated by the perl XS compiler twiddles with the stack on 95 * entry to an XSUB. 96 */ 97 98 MODULE = Sun::Solaris::Project PACKAGE = Sun::Solaris::Project 99 PROTOTYPES: ENABLE 100 101 # 102 # Define any constants that need to be exported. By doing it this way we can 103 # avoid the overhead of using the DynaLoader package, and in addition constants 104 # defined using this mechanism are eligible for inlining by the perl 105 # interpreter at compile time. 106 # 107 BOOT: 108 { 109 HV *stash; 110 char buf[128]; 111 stash = gv_stashpv("Sun::Solaris::Project", TRUE); 112 newCONSTSUB(stash, "MAXPROJID", newSViv(MAXPROJID)); 113 newCONSTSUB(stash, "PROJNAME_MAX", newSViv(PROJNAME_MAX)); 114 newCONSTSUB(stash, "PROJF_PATH", 115 newSVpv(PROJF_PATH, sizeof (PROJF_PATH) - 1)); 116 newCONSTSUB(stash, "PROJECT_BUFSZ", newSViv(PROJECT_BUFSZ)); 117 newCONSTSUB(stash, "SETPROJ_ERR_TASK", newSViv(SETPROJ_ERR_TASK)); 118 newCONSTSUB(stash, "SETPROJ_ERR_POOL", newSViv(SETPROJ_ERR_POOL)); 119 newCONSTSUB(stash, "RCTL_GLOBAL_NOBASIC", 120 newSViv(RCTL_GLOBAL_NOBASIC)); 121 newCONSTSUB(stash, "RCTL_GLOBAL_LOWERABLE", 122 newSViv(RCTL_GLOBAL_LOWERABLE)); 123 newCONSTSUB(stash, "RCTL_GLOBAL_DENY_ALWAYS", 124 newSViv(RCTL_GLOBAL_DENY_ALWAYS)); 125 newCONSTSUB(stash, "RCTL_GLOBAL_DENY_NEVER", 126 newSViv(RCTL_GLOBAL_DENY_NEVER)); 127 newCONSTSUB(stash, "RCTL_GLOBAL_FILE_SIZE", 128 newSViv(RCTL_GLOBAL_FILE_SIZE)); 129 newCONSTSUB(stash, "RCTL_GLOBAL_CPU_TIME", 130 newSViv(RCTL_GLOBAL_CPU_TIME)); 131 newCONSTSUB(stash, "RCTL_GLOBAL_SIGNAL_NEVER", 132 newSViv(RCTL_GLOBAL_SIGNAL_NEVER)); 133 newCONSTSUB(stash, "RCTL_GLOBAL_INFINITE", 134 newSViv(RCTL_GLOBAL_INFINITE)); 135 newCONSTSUB(stash, "RCTL_GLOBAL_UNOBSERVABLE", 136 newSViv(RCTL_GLOBAL_UNOBSERVABLE)); 137 newCONSTSUB(stash, "RCTL_GLOBAL_BYTES", 138 newSViv(RCTL_GLOBAL_BYTES)); 139 newCONSTSUB(stash, "RCTL_GLOBAL_SECONDS", 140 newSViv(RCTL_GLOBAL_SECONDS)); 141 newCONSTSUB(stash, "RCTL_GLOBAL_COUNT", 142 newSViv(RCTL_GLOBAL_COUNT)); 143 sprintf(buf, "%llu", UINT64_MAX); 144 newCONSTSUB(stash, "RCTL_MAX_VALUE", 145 newSVpv(buf, strlen(buf))); 146 } 147 148 projid_t 149 getprojid() 150 151 int 152 setproject(name, user_name, flags) 153 const char *name; 154 const char *user_name 155 uint_t flags 156 157 void 158 activeprojects() 159 PREINIT: 160 int nitems; 161 PPCODE: 162 PUTBACK; 163 nitems = 0; 164 project_walk(&pwalk_cb, (void*)&nitems); 165 XSRETURN(nitems); 166 167 void 168 getprojent() 169 PREINIT: 170 struct project proj, *projp; 171 char buf[PROJECT_BUFSZ]; 172 PPCODE: 173 PUTBACK; 174 if ((projp = getprojent(&proj, buf, sizeof (buf)))) { 175 XSRETURN(pushret_project(projp)); 176 } else { 177 XSRETURN_EMPTY; 178 } 179 180 void 181 setprojent() 182 183 void 184 endprojent() 185 186 void 187 getprojbyname(name) 188 char *name 189 PREINIT: 190 struct project proj, *projp; 191 char buf[PROJECT_BUFSZ]; 192 PPCODE: 193 PUTBACK; 194 if ((projp = getprojbyname(name, &proj, buf, sizeof (buf)))) { 195 XSRETURN(pushret_project(projp)); 196 } else { 197 XSRETURN_EMPTY; 198 } 199 200 void 201 getprojbyid(id) 202 projid_t id 203 PREINIT: 204 struct project proj, *projp; 205 char buf[PROJECT_BUFSZ]; 206 PPCODE: 207 PUTBACK; 208 if ((projp = getprojbyid(id, &proj, buf, sizeof (buf)))) { 209 XSRETURN(pushret_project(projp)); 210 } else { 211 XSRETURN_EMPTY; 212 } 213 214 void 215 getdefaultproj(user) 216 char *user 217 PREINIT: 218 struct project proj, *projp; 219 char buf[PROJECT_BUFSZ]; 220 PPCODE: 221 PUTBACK; 222 if ((projp = getdefaultproj(user, &proj, buf, sizeof (buf)))) { 223 XSRETURN(pushret_project(projp)); 224 } else { 225 XSRETURN_EMPTY; 226 } 227 228 void 229 fgetprojent(fh) 230 FILE *fh 231 PREINIT: 232 struct project proj, *projp; 233 char buf[PROJECT_BUFSZ]; 234 PPCODE: 235 PUTBACK; 236 if ((projp = fgetprojent(fh, &proj, buf, sizeof (buf)))) { 237 XSRETURN(pushret_project(projp)); 238 } else { 239 XSRETURN_EMPTY; 240 } 241 242 bool 243 inproj(user, proj) 244 char *user 245 char *proj 246 PREINIT: 247 char buf[PROJECT_BUFSZ]; 248 CODE: 249 RETVAL = inproj(user, proj, buf, sizeof (buf)); 250 OUTPUT: 251 RETVAL 252 253 254 int 255 getprojidbyname(proj) 256 char *proj 257 PREINIT: 258 int id; 259 PPCODE: 260 if ((id = getprojidbyname(proj)) == -1) { 261 XSRETURN_UNDEF; 262 } else { 263 XSRETURN_IV(id); 264 } 265 266 267 # rctl_get_info(name) 268 # 269 # For the given rctl name, returns the list 270 # ($max, $flags), where $max is the integer value 271 # of the system rctl, and $flags are the rctl's 272 # global flags, as returned by rctlblk_get_global_flags 273 # 274 # This function is private to Project.pm 275 void 276 rctl_get_info(name) 277 char *name 278 PREINIT: 279 rctlblk_t *blk1 = NULL; 280 rctlblk_t *blk2 = NULL; 281 rctlblk_t *tmp = NULL; 282 rctl_priv_t priv; 283 rctl_qty_t value; 284 int flags = 0; 285 int ret; 286 int err = 0; 287 char string[24]; /* 24 will always hold a uint64_t */ 288 PPCODE: 289 Newc(0, blk1, rctlblk_size(), char, rctlblk_t); 290 if (blk1 == NULL) { 291 err = 1; 292 goto out; 293 } 294 Newc(1, blk2, rctlblk_size(), char, rctlblk_t); 295 if (blk2 == NULL) { 296 err = 1; 297 goto out; 298 } 299 ret = getrctl(name, NULL, blk1, RCTL_FIRST); 300 if (ret != 0) { 301 err = 1; 302 goto out; 303 } 304 priv = rctlblk_get_privilege(blk1); 305 while (priv != RCPRIV_SYSTEM) { 306 tmp = blk2; 307 blk2 = blk1; 308 blk1 = tmp; 309 ret = getrctl(name, blk2, blk1, RCTL_NEXT); 310 if (ret != 0) { 311 err = 1; 312 goto out; 313 } 314 priv = rctlblk_get_privilege(blk1); 315 } 316 value = rctlblk_get_value(blk1); 317 flags = rctlblk_get_global_flags(blk1); 318 ret = sprintf(string, "%llu", value); 319 if (ret <= 0) { 320 err = 1; 321 } 322 out: 323 if (blk1) 324 Safefree(blk1); 325 if (blk2) 326 Safefree(blk2); 327 if (err) 328 XSRETURN(0); 329 330 XPUSHs(sv_2mortal(newSVpv(string, 0))); 331 XPUSHs(sv_2mortal(newSViv(flags))); 332 XSRETURN(2); 333 334 # 335 # pool_exists(name) 336 # 337 # Returns 0 a pool with the given name exists on the current system. 338 # Returns 1 if pools are disabled or the pool does not exist 339 # 340 # Used internally by project.pm to validate the project.pool attribute 341 # 342 # This function is private to Project.pm 343 void 344 pool_exists(name) 345 char *name 346 PREINIT: 347 pool_conf_t *conf; 348 pool_t *pool; 349 pool_status_t status; 350 int fd; 351 PPCODE: 352 353 /* 354 * Determine if pools are enabled using /dev/pool directly, as 355 * libpool may not be present. 356 */ 357 if (getzoneid() != GLOBAL_ZONEID) { 358 XSRETURN_IV(1); 359 } 360 if ((fd = open("/dev/pool", O_RDONLY)) < 0) { 361 XSRETURN_IV(1); 362 } 363 if (ioctl(fd, POOL_STATUSQ, &status) < 0) { 364 (void) close(fd); 365 XSRETURN_IV(1); 366 } 367 close(fd); 368 if (status.ps_io_state != 1) { 369 XSRETURN_IV(1); 370 } 371 372 /* 373 * If pools are enabled, assume libpool is present. 374 */ 375 conf = pool_conf_alloc(); 376 if (conf == NULL) { 377 XSRETURN_IV(1); 378 } 379 if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY)) { 380 pool_conf_free(conf); 381 XSRETURN_IV(1); 382 } 383 pool = pool_get_pool(conf, name); 384 if (pool == NULL) { 385 pool_conf_close(conf); 386 pool_conf_free(conf); 387 XSRETURN_IV(1); 388 } 389 pool_conf_close(conf); 390 pool_conf_free(conf); 391 XSRETURN_IV(0); 392 393