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