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 #include <stdio.h>
23 #include <stdlib.h>
24 #include <libintl.h>
25 #include <locale.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <stddef.h>
29 #include <sys/types.h>
30 #include <sys/task.h>
31
32 #include <sys/debug.h>
33
34
35 #include "projent.h"
36 #include "util.h"
37
38 #define SEQU(str1, str2) (strcmp(str1, str2) == 0)
39
40 #define CHECK_ERRORS_FREE_PLST(errlst, plst, attrs, ecode) { \
41 if (!lst_is_empty(errlst)) { \
42 util_print_errmsgs(errlst); \
43 if (plst != NULL) { \
44 projent_free_lst(plst); \
45 free(plst); \
46 } \
47 free(attrs); \
48 usage(); \
49 exit(ecode); \
50 } \
51 }
52
53
54 /*
55 * Print usage
56 */
57 static void
58 usage(void)
59 {
60 (void) fprintf(stderr, gettext(
61 "Usage:\n"
62 "projmod [-n] [-A|-f filename] [-p projid [-o]] [-c comment]\n"
63 " [-a|-s|-r] [-U user[,user...]] [-G group[,group...]]\n"
64 " [-K name[=value[,value...]]] [-l new_projectname]\n"
65 " project\n"));
66 }
67
68
69 /*
70 * main()
71 */
72 int
73 main(int argc, char **argv)
74 {
75 int e, c, error;
76
77 extern char *optarg;
78 extern int optind, optopt;
79 lst_t *plst = NULL;
80 int flags = 0;
81 projent_t *ent, *modent;
82
83 /* Command line options */
84 boolean_t fflag, nflag, cflag, oflag, pflag, lflag;
85 boolean_t sflag, rflag, aflag;
86 boolean_t Uflag, Gflag, Kflag, Aflag;
87 boolean_t modify;
88
89 /* Project entry fields */
90 char *pname, *npname;
91 char *comment, *users, *groups, *attrs;
92 char *pusers, *pgroups;
93
94 lst_t *pattribs;
95
96 lst_t errlst;
97
98 /* Project file defaults to system project file "/etc/project" */
99 char *projfile = PROJF_PATH;
100 struct project proj, *projp;
101 char buf[PROJECT_BUFSZ];
102 char *str;
103
104 comment = users = groups = "";
105 pname = npname = NULL;
106
107 fflag = nflag = cflag = oflag = pflag = lflag = B_FALSE;
108 sflag = rflag = aflag = B_FALSE;
109 Uflag = Gflag = Kflag = Aflag = B_FALSE;
110
111 modify = B_FALSE;
112
113
114
115 attrs = util_safe_zmalloc(1);
116 lst_create(&errlst);
117
118
119 (void) setlocale(LC_ALL, "");
120 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
121 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
122 #endif
123 (void) textdomain(TEXT_DOMAIN);
124
125 /* Parse the command line argument list */
126 while ((c = getopt(argc, argv, ":hf:nc:op:l:sraU:G:K:A")) != EOF)
127 switch (c) {
128 case 'h':
129 usage();
130 exit(0);
131 break;
132 case 'f':
133 fflag = B_TRUE;
134 projfile = optarg;
135 break;
136 case 'n':
137 nflag = B_TRUE;
138 break;
139 case 'c':
140 cflag = B_TRUE;
141 comment = optarg;
142 break;
143 case 'o':
144 oflag = B_TRUE;
145 break;
146 case 'p':
147 pflag = B_TRUE;
148 break;
149 case 'l':
150 lflag = B_TRUE;
151 npname = optarg;
152 break;
153 case 's':
154 sflag = B_TRUE;
155 break;
156 case 'r':
157 rflag = B_TRUE;
158 break;
159 case 'a':
160 aflag = B_TRUE;
161 break;
162 case 'U':
163 Uflag = B_TRUE;
164 users = optarg;
165 break;
166 case 'G':
167 Gflag = B_TRUE;
168 groups = optarg;
169 break;
170 case 'K':
171 Kflag = B_TRUE;
172 attrs = UTIL_STR_APPEND2(attrs, ";", optarg);
173 break;
174 case 'A':
175 Aflag = B_TRUE;
176 break;
177 default:
178 util_add_errmsg(&errlst, gettext(
179 "Invalid option: -%c"), optopt);
180 break;
181 }
182
183 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
184
185 if (optind == argc - 1) {
186 pname = argv[optind];
187 }
188
189 if (cflag || Gflag || lflag || pflag || Uflag || Kflag || Aflag) {
190 modify = B_TRUE;
191 if (pname == NULL) {
192 util_add_errmsg(&errlst, gettext(
193 "No project name specified"));
194 }
195 } else if (pname != NULL) {
196 util_add_errmsg(&errlst, gettext(
197 "missing -c, -G, -l, -p, -U, or -K"));
198 }
199
200 if (Aflag && fflag) {
201 util_add_errmsg(&errlst, gettext(
202 "-A and -f are mutually exclusive"));
203 }
204
205 if (oflag && !pflag) {
206 util_add_errmsg(&errlst, gettext(
207 "-o requires -p projid to be specified"));
208 }
209
210 if ((aflag && (rflag || sflag)) || (rflag && (aflag || sflag)) ||
211 (sflag && (aflag || rflag))) {
212 util_add_errmsg(&errlst, gettext(
213 "-a, -r, and -s are mutually exclusive"));
214 }
215
216 if ((aflag || rflag || sflag) && !(Uflag || Gflag || Kflag)) {
217 util_add_errmsg(&errlst, gettext(
218 "-a, -r, and -s require -U users, -G groups "
219 "or -K attributes to be specified"));
220 }
221
222 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
223
224 if (aflag) {
225 flags |= F_MOD_ADD;
226 } else if (rflag) {
227 flags |= F_MOD_REM;
228 } else if (sflag) {
229 flags |= F_MOD_SUB;
230 } else {
231 flags |= F_MOD_REP;
232 }
233 if (!nflag) {
234 flags |= F_PAR_VLD;
235 }
236 flags |= F_PAR_RES | F_PAR_DUP;
237
238 plst = projent_get_lst(projfile, flags, &errlst);
239 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
240
241 modent = NULL;
242 if (pname != NULL) {
243 for (e = 0; e < lst_size(plst); e++) {
244 ent = lst_at(plst, e);
245 if (SEQU(ent->projname, pname)) {
246 modent = ent;
247 }
248 }
249 if (modent == NULL) {
250 util_add_errmsg(&errlst, gettext(
251 "Project \"%s\" does not exist"), pname);
252 }
253 }
254
255 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
256
257 /*
258 * If there is no modification options, simply reading the file, which
259 * includes parsing and verifying, is sufficient.
260 */
261 if (!modify) {
262 exit(0);
263 }
264
265 VERIFY(modent != NULL);
266
267 if (lflag && projent_parse_name(pname, &errlst) == 0 &&
268 projent_validate_unique_name(plst, npname, &errlst) == 0) {
269 free(modent->projname);
270 modent->projname = util_safe_strdup(npname);
271 }
272
273 if (cflag && projent_parse_comment(comment, &errlst) == 0) {
274 free(modent->comment);
275 modent->comment = util_safe_strdup(comment);
276 }
277
278 if (Uflag) {
279 pusers = projent_parse_users(users, F_PAR_SPC,
280 &errlst);
281 if (pusers != NULL) {
282 projent_merge_usrgrp("user", &modent->userlist,
283 pusers, flags, &errlst);
284 free(pusers);
285 }
286 }
287
288 if (Gflag) {
289 pgroups = projent_parse_groups(groups, F_PAR_SPC,
290 &errlst);
291 if (pgroups != NULL) {
292 projent_merge_usrgrp("group", &modent->grouplist,
293 pgroups, flags, &errlst);
294 free(pgroups);
295 }
296 }
297
298 if (Kflag) {
299 pattribs = projent_parse_attributes(attrs, F_PAR_UNT, &errlst);
300 if (pattribs != NULL) {
301 projent_merge_attributes(&modent->attrs,
302 pattribs, flags, &errlst);
303 projent_sort_attributes(modent->attrs);
304 projent_free_attributes(pattribs);
305 UTIL_FREE_SNULL(pattribs);
306 }
307 }
308
309 if (!nflag) {
310 (void) projent_validate(modent, flags, &errlst);
311 }
312 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
313
314 if (modify) {
315 projent_put_lst(projfile, plst, &errlst);
316 }
317 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
318
319 if (Aflag && (error = setproject(pname, "root",
320 TASK_FINAL|TASK_PROJ_PURGE)) != 0) {
321 if (error == SETPROJ_ERR_TASK) {
322 if (errno == EAGAIN) {
323 util_add_errmsg(&errlst, gettext(
324 "resource control limit has been reached"));
325 } else if (errno == ESRCH) {
326 util_add_errmsg(&errlst, gettext(
327 "user \"%s\" is not a member "
328 "of project \"%s\""), "root", pname);
329 } else {
330 util_add_errmsg(&errlst, gettext(
331 "could not join project \"%s\""), pname);
332 }
333 } else if (error == SETPROJ_ERR_POOL) {
334 if (errno == EACCES) {
335 util_add_errmsg(&errlst, gettext(
336 "no resource pool accepting default "
337 "bindings exists for project \"%s\""),
338 pname);
339 } else if (errno == ESRCH) {
340 util_add_errmsg(&errlst, gettext(
341 "specified resource pool does not exist "
342 "for project \"%s\""), pname);
343 } else {
344 util_add_errmsg(&errlst, gettext(
345 "could not bind to default resource pool "
346 "for project \"%s\""), pname);
347 }
348 } else {
349 /*
350 * error represents the position - within the
351 * semi-colon delimited attribute - that generated
352 * the error.
353 */
354 if (error <= 0) {
355 util_add_errmsg(&errlst, gettext(
356 "setproject failed for project \"%s\""),
357 pname);
358 } else {
359 /* To be completed */
360 projp = getprojbyname(pname, &proj, buf,
361 sizeof (buf));
362 pattribs = (projp != NULL) ?
363 projent_parse_attributes(projp->pj_attr,
364 0, &errlst) : NULL;
365 if (projp != NULL && pattribs != NULL &&
366 (str = projent_attrib_tostring(
367 lst_at(pattribs, error - 1))) != NULL) {
368 util_add_errmsg(&errlst, gettext(
369 "warning, \"%s\" resource control "
370 "assignment failed for project "
371 "\"%s\""), str, pname);
372 free(str);
373 } else {
374 util_add_errmsg(&errlst, gettext(
375 "warning, resource control "
376 "assignment failed for project "
377 "\"%s\" attribute %d"), pname,
378 error);
379 }
380
381 if (pattribs != NULL) {
382 projent_free_attributes(pattribs);
383 UTIL_FREE_SNULL(pattribs);
384 }
385 }
386 }
387 }
388
389 CHECK_ERRORS_FREE_PLST(&errlst, plst, attrs, 2);
390
391 return (0);
392 }