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 }