Print this page
4078 groupadd execs getent unnecessarily
Reviewed by: Rich Lowe <richlowe@richlowe.net>
Reviewed by: Gary Mills <gary_mills@fastmail.fm>
Reviewed by: Milan Jurik <milan.jurik@xylab.cz>
Reviewed by: Gordon Ross <Gordon.W.Ross@gmail.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/oamuser/user/usermod.c
+++ new/usr/src/cmd/oamuser/user/usermod.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 27 /* All Rights Reserved */
28 28
29 -
29 +/*
30 + * Copyright (c) 2013 RackTop Systems.
31 + */
30 32
31 33 #include <sys/types.h>
32 34 #include <sys/stat.h>
33 35 #include <sys/param.h>
34 36 #include <stdio.h>
35 37 #include <stdlib.h>
36 38 #include <ctype.h>
37 39 #include <limits.h>
38 40 #include <string.h>
39 41 #include <userdefs.h>
40 42 #include <user_attr.h>
41 43 #include <nss_dbdefs.h>
42 44 #include <errno.h>
43 45 #include <project.h>
44 46 #include "users.h"
45 47 #include "messages.h"
46 48 #include "funcs.h"
47 49
48 50 /*
49 51 * usermod [-u uid [-o] | -g group | -G group [[,group]...] | -d dir [-m]
50 52 * | -s shell | -c comment | -l new_logname]
51 53 * | -f inactive | -e expire ]
52 54 * [ -A authorization [, authorization ...]]
53 55 * [ -P profile [, profile ...]]
54 56 * [ -R role [, role ...]]
55 57 * [ -K key=value ]
56 58 * [ -p project [, project]] login
57 59 *
58 60 * This command adds new user logins to the system. Arguments are:
59 61 *
60 62 * uid - an integer less than MAXUID
61 63 * group - an existing group's integer ID or char string name
62 64 * dir - a directory
63 65 * shell - a program to be used as a shell
64 66 * comment - any text string
65 67 * skel_dir - a directory
66 68 * base_dir - a directory
67 69 * rid - an integer less than 2**16 (USHORT)
68 70 * login - a string of printable chars except colon (:)
69 71 * inactive - number of days a login maybe inactive before it is locked
70 72 * expire - date when a login is no longer valid
71 73 * authorization - One or more comma separated authorizations defined
72 74 * in auth_attr(4).
73 75 * profile - One or more comma separated execution profiles defined
74 76 * in prof_attr(4)
75 77 * role - One or more comma-separated role names defined in user_attr(4)
76 78 * key=value - One or more -K options each specifying a valid user_attr(4)
77 79 * attribute.
78 80 *
79 81 */
80 82
81 83 extern int **valid_lgroup(), isbusy();
82 84 extern int valid_uid(), check_perm(), create_home(), move_dir();
83 85 extern int valid_expire(), edit_group(), call_passmgmt();
84 86 extern projid_t **valid_lproject();
85 87
86 88 static uid_t uid; /* new uid */
87 89 static gid_t gid; /* gid of new login */
88 90 static char *new_logname = NULL; /* new login name with -l option */
89 91 static char *uidstr = NULL; /* uid from command line */
90 92 static char *group = NULL; /* group from command line */
91 93 static char *grps = NULL; /* multi groups from command line */
92 94 static char *dir = NULL; /* home dir from command line */
93 95 static char *shell = NULL; /* shell from command line */
94 96 static char *comment = NULL; /* comment from command line */
95 97 static char *logname = NULL; /* login name to add */
96 98 static char *inactstr = NULL; /* inactive from command line */
97 99 static char *expire = NULL; /* expiration date from command line */
98 100 static char *projects = NULL; /* project ids from command line */
99 101 static char *usertype;
100 102
101 103 char *cmdname;
102 104 static char gidstring[32], uidstring[32];
103 105 char inactstring[10];
104 106
105 107 char *
106 108 strcpmalloc(str)
107 109 char *str;
108 110 {
109 111 if (str == NULL)
110 112 return (NULL);
111 113
112 114 return (strdup(str));
113 115 }
114 116 struct passwd *
115 117 passwd_cpmalloc(opw)
116 118 struct passwd *opw;
117 119 {
118 120 struct passwd *npw;
119 121
120 122 if (opw == NULL)
121 123 return (NULL);
122 124
123 125
124 126 npw = malloc(sizeof (struct passwd));
125 127
126 128 npw->pw_name = strcpmalloc(opw->pw_name);
127 129 npw->pw_passwd = strcpmalloc(opw->pw_passwd);
128 130 npw->pw_uid = opw->pw_uid;
129 131 npw->pw_gid = opw->pw_gid;
130 132 npw->pw_age = strcpmalloc(opw->pw_age);
131 133 npw->pw_comment = strcpmalloc(opw->pw_comment);
132 134 npw->pw_gecos = strcpmalloc(opw->pw_gecos);
133 135 npw->pw_dir = strcpmalloc(opw->pw_dir);
134 136 npw->pw_shell = strcpmalloc(opw->pw_shell);
135 137
136 138 return (npw);
137 139 }
138 140
139 141 int
140 142 main(argc, argv)
141 143 int argc;
142 144 char **argv;
143 145 {
144 146 int ch, ret = EX_SUCCESS, call_pass = 0, oflag = 0;
145 147 int tries, mflag = 0, inact, **gidlist, flag = 0;
146 148 boolean_t fail_if_busy = B_FALSE;
147 149 char *ptr;
148 150 struct passwd *pstruct; /* password struct for login */
149 151 struct passwd *pw;
150 152 struct group *g_ptr; /* validated group from -g */
151 153 struct stat statbuf; /* status buffer for stat */
152 154 #ifndef att
153 155 FILE *pwf; /* fille ptr for opened passwd file */
154 156 #endif
155 157 int warning;
156 158 projid_t **projlist;
157 159 char **nargv; /* arguments for execvp of passmgmt */
158 160 int argindex; /* argument index into nargv */
159 161 userattr_t *ua;
160 162 char *val;
161 163 int isrole; /* current account is role */
162 164
163 165 cmdname = argv[0];
164 166
165 167 if (geteuid() != 0) {
166 168 errmsg(M_PERM_DENIED);
167 169 exit(EX_NO_PERM);
168 170 }
169 171
170 172 opterr = 0; /* no print errors from getopt */
171 173 /* get user type based on the program name */
172 174 usertype = getusertype(argv[0]);
173 175
174 176 while ((ch = getopt(argc, argv,
175 177 "c:d:e:f:G:g:l:mop:s:u:A:P:R:K:")) != EOF)
176 178 switch (ch) {
177 179 case 'c':
178 180 comment = optarg;
179 181 flag++;
180 182 break;
181 183 case 'd':
182 184 dir = optarg;
183 185 fail_if_busy = B_TRUE;
184 186 flag++;
185 187 break;
186 188 case 'e':
187 189 expire = optarg;
188 190 flag++;
189 191 break;
190 192 case 'f':
191 193 inactstr = optarg;
192 194 flag++;
193 195 break;
194 196 case 'G':
195 197 grps = optarg;
196 198 flag++;
197 199 break;
198 200 case 'g':
199 201 group = optarg;
200 202 fail_if_busy = B_TRUE;
201 203 flag++;
202 204 break;
203 205 case 'l':
204 206 new_logname = optarg;
205 207 fail_if_busy = B_TRUE;
206 208 flag++;
207 209 break;
208 210 case 'm':
209 211 mflag++;
210 212 flag++;
211 213 fail_if_busy = B_TRUE;
212 214 break;
213 215 case 'o':
214 216 oflag++;
215 217 flag++;
216 218 fail_if_busy = B_TRUE;
217 219 break;
218 220 case 'p':
219 221 projects = optarg;
220 222 flag++;
221 223 break;
222 224 case 's':
223 225 shell = optarg;
224 226 flag++;
225 227 break;
226 228 case 'u':
227 229 uidstr = optarg;
228 230 flag++;
229 231 fail_if_busy = B_TRUE;
230 232 break;
231 233 case 'A':
232 234 change_key(USERATTR_AUTHS_KW, optarg);
233 235 flag++;
234 236 break;
235 237 case 'P':
236 238 change_key(USERATTR_PROFILES_KW, optarg);
237 239 flag++;
238 240 break;
239 241 case 'R':
240 242 change_key(USERATTR_ROLES_KW, optarg);
241 243 flag++;
242 244 break;
243 245 case 'K':
244 246 change_key(NULL, optarg);
245 247 flag++;
246 248 break;
247 249 default:
248 250 case '?':
249 251 if (is_role(usertype))
250 252 errmsg(M_MRUSAGE);
251 253 else
252 254 errmsg(M_MUSAGE);
253 255 exit(EX_SYNTAX);
254 256 }
255 257
256 258 if (optind != argc - 1 || flag == 0) {
257 259 if (is_role(usertype))
258 260 errmsg(M_MRUSAGE);
259 261 else
260 262 errmsg(M_MUSAGE);
261 263 exit(EX_SYNTAX);
262 264 }
263 265
264 266 if ((!uidstr && oflag) || (mflag && !dir)) {
265 267 if (is_role(usertype))
266 268 errmsg(M_MRUSAGE);
267 269 else
268 270 errmsg(M_MUSAGE);
269 271 exit(EX_SYNTAX);
270 272 }
271 273
272 274 logname = argv[optind];
273 275
274 276 /* Determine whether the account is a role or not */
275 277 if ((ua = getusernam(logname)) == NULL ||
276 278 (val = kva_match(ua->attr, USERATTR_TYPE_KW)) == NULL ||
277 279 strcmp(val, USERATTR_TYPE_NONADMIN_KW) != 0)
278 280 isrole = 0;
279 281 else
280 282 isrole = 1;
281 283
282 284 /* Verify that rolemod is used for roles and usermod for users */
283 285 if (isrole != is_role(usertype)) {
284 286 if (isrole)
285 287 errmsg(M_ISROLE);
286 288 else
287 289 errmsg(M_ISUSER);
288 290 exit(EX_SYNTAX);
289 291 }
290 292
291 293 /* Set the usertype key; defaults to the commandline */
292 294 usertype = getsetdefval(USERATTR_TYPE_KW, usertype);
293 295
294 296 if (is_role(usertype)) {
295 297 /* Roles can't have roles */
296 298 if (getsetdefval(USERATTR_ROLES_KW, NULL) != NULL) {
297 299 errmsg(M_MRUSAGE);
298 300 exit(EX_SYNTAX);
299 301 }
300 302 /* If it was an ordinary user, delete its roles */
301 303 if (!isrole)
302 304 change_key(USERATTR_ROLES_KW, "");
303 305 }
304 306
305 307 #ifdef att
306 308 pw = getpwnam(logname);
307 309 #else
308 310 /*
309 311 * Do this with fgetpwent to make sure we are only looking on local
310 312 * system (since passmgmt only works on local system).
311 313 */
312 314 if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
313 315 errmsg(M_OOPS, "open", "/etc/passwd");
314 316 exit(EX_FAILURE);
315 317 }
316 318 while ((pw = fgetpwent(pwf)) != NULL)
317 319 if (strcmp(pw->pw_name, logname) == 0)
318 320 break;
319 321
320 322 fclose(pwf);
321 323 #endif
322 324
323 325 if (pw == NULL) {
324 326 char pwdb[NSS_BUFLEN_PASSWD];
325 327 struct passwd pwd;
326 328
327 329 if (getpwnam_r(logname, &pwd, pwdb, sizeof (pwdb)) == NULL) {
328 330 /* This user does not exist. */
329 331 errmsg(M_EXIST, logname);
330 332 exit(EX_NAME_NOT_EXIST);
331 333 } else {
332 334 /* This user exists in non-local name service. */
333 335 errmsg(M_NONLOCAL, logname);
334 336 exit(EX_NOT_LOCAL);
335 337 }
336 338 }
337 339
338 340 pstruct = passwd_cpmalloc(pw);
339 341
340 342 /*
341 343 * We can't modify a logged in user if any of the following
342 344 * are being changed:
343 345 * uid (-u & -o), group (-g), home dir (-m), loginname (-l).
344 346 * If none of those are specified it is okay to go ahead
345 347 * some types of changes only take effect on next login, some
346 348 * like authorisations and profiles take effect instantly.
347 349 * One might think that -K type=role should require that the
348 350 * user not be logged in, however this would make it very
349 351 * difficult to make the root account a role using this command.
350 352 */
351 353 if (isbusy(logname)) {
352 354 if (fail_if_busy) {
353 355 errmsg(M_BUSY, logname, "change");
354 356 exit(EX_BUSY);
355 357 }
356 358 warningmsg(WARN_LOGGED_IN, logname);
357 359 }
358 360
359 361 if (new_logname && strcmp(new_logname, logname)) {
360 362 switch (valid_login(new_logname, (struct passwd **)NULL,
361 363 &warning)) {
362 364 case INVALID:
363 365 errmsg(M_INVALID, new_logname, "login name");
364 366 exit(EX_BADARG);
365 367 /*NOTREACHED*/
366 368
367 369 case NOTUNIQUE:
368 370 errmsg(M_USED, new_logname);
369 371 exit(EX_NAME_EXISTS);
370 372 /*NOTREACHED*/
371 373 default:
372 374 call_pass = 1;
373 375 break;
374 376 }
375 377 if (warning)
376 378 warningmsg(warning, logname);
377 379 }
378 380
379 381 if (uidstr) {
380 382 /* convert uidstr to integer */
381 383 errno = 0;
382 384 uid = (uid_t)strtol(uidstr, &ptr, (int)10);
383 385 if (*ptr || errno == ERANGE) {
384 386 errmsg(M_INVALID, uidstr, "user id");
385 387 exit(EX_BADARG);
386 388 }
387 389
388 390 if (uid != pstruct->pw_uid) {
389 391 switch (valid_uid(uid, NULL)) {
390 392 case NOTUNIQUE:
391 393 if (!oflag) {
392 394 /* override not specified */
393 395 errmsg(M_UID_USED, uid);
394 396 exit(EX_ID_EXISTS);
395 397 }
396 398 break;
397 399 case RESERVED:
398 400 errmsg(M_RESERVED, uid);
399 401 break;
400 402 case TOOBIG:
401 403 errmsg(M_TOOBIG, "uid", uid);
402 404 exit(EX_BADARG);
403 405 break;
404 406 }
405 407
406 408 call_pass = 1;
407 409
408 410 } else {
409 411 /* uid's the same, so don't change anything */
410 412 uidstr = NULL;
411 413 oflag = 0;
412 414 }
413 415
414 416 } else uid = pstruct->pw_uid;
415 417
416 418 if (group) {
417 419 switch (valid_group(group, &g_ptr, &warning)) {
418 420 case INVALID:
419 421 errmsg(M_INVALID, group, "group id");
420 422 exit(EX_BADARG);
421 423 /*NOTREACHED*/
422 424 case TOOBIG:
423 425 errmsg(M_TOOBIG, "gid", group);
424 426 exit(EX_BADARG);
425 427 /*NOTREACHED*/
426 428 case UNIQUE:
427 429 errmsg(M_GRP_NOTUSED, group);
428 430 exit(EX_NAME_NOT_EXIST);
429 431 /*NOTREACHED*/
430 432 case RESERVED:
431 433 gid = (gid_t)strtol(group, &ptr, (int)10);
432 434 errmsg(M_RESERVED_GID, gid);
433 435 break;
434 436 }
435 437 if (warning)
436 438 warningmsg(warning, group);
437 439
438 440 if (g_ptr != NULL)
439 441 gid = g_ptr->gr_gid;
440 442 else
441 443 gid = pstruct->pw_gid;
442 444
443 445 /* call passmgmt if gid is different, else ignore group */
444 446 if (gid != pstruct->pw_gid)
445 447 call_pass = 1;
446 448 else group = NULL;
447 449
448 450 } else gid = pstruct->pw_gid;
449 451
450 452 if (grps && *grps) {
451 453 if (!(gidlist = valid_lgroup(grps, gid)))
452 454 exit(EX_BADARG);
453 455 } else
454 456 gidlist = (int **)0;
455 457
456 458 if (projects && *projects) {
457 459 if (! (projlist = valid_lproject(projects)))
458 460 exit(EX_BADARG);
459 461 } else
460 462 projlist = (projid_t **)0;
461 463
462 464 if (dir) {
463 465 if (REL_PATH(dir)) {
464 466 errmsg(M_RELPATH, dir);
465 467 exit(EX_BADARG);
466 468 }
467 469 if (strcmp(pstruct->pw_dir, dir) == 0) {
468 470 /* home directory is the same so ignore dflag & mflag */
469 471 dir = NULL;
470 472 mflag = 0;
471 473 } else call_pass = 1;
472 474 }
473 475
474 476 if (mflag) {
475 477 if (stat(dir, &statbuf) == 0) {
476 478 /* Home directory exists */
477 479 if (check_perm(statbuf, pstruct->pw_uid,
478 480 pstruct->pw_gid, S_IWOTH|S_IXOTH) != 0) {
479 481 errmsg(M_NO_PERM, logname, dir);
480 482 exit(EX_NO_PERM);
481 483 }
482 484
483 485 } else ret = create_home(dir, NULL, uid, gid);
484 486
485 487 if (ret == EX_SUCCESS)
486 488 ret = move_dir(pstruct->pw_dir, dir, logname);
487 489
488 490 if (ret != EX_SUCCESS)
489 491 exit(ret);
490 492 }
491 493
492 494 if (shell) {
493 495 if (REL_PATH(shell)) {
494 496 errmsg(M_RELPATH, shell);
495 497 exit(EX_BADARG);
496 498 }
497 499 if (strcmp(pstruct->pw_shell, shell) == 0) {
498 500 /* ignore s option if shell is not different */
499 501 shell = NULL;
500 502 } else {
501 503 if (stat(shell, &statbuf) < 0 ||
502 504 (statbuf.st_mode & S_IFMT) != S_IFREG ||
↓ open down ↓ |
463 lines elided |
↑ open up ↑ |
503 505 (statbuf.st_mode & 0555) != 0555) {
504 506
505 507 errmsg(M_INVALID, shell, "shell");
506 508 exit(EX_BADARG);
507 509 }
508 510
509 511 call_pass = 1;
510 512 }
511 513 }
512 514
513 - if (comment)
515 + if (comment) {
514 516 /* ignore comment if comment is not changed */
515 517 if (strcmp(pstruct->pw_comment, comment))
516 518 call_pass = 1;
517 519 else
518 520 comment = NULL;
521 + }
519 522
520 523 /* inactive string is a positive integer */
521 524 if (inactstr) {
522 525 /* convert inactstr to integer */
523 526 inact = (int)strtol(inactstr, &ptr, 10);
524 527 if (*ptr || inact < 0) {
525 528 errmsg(M_INVALID, inactstr, "inactivity period");
526 529 exit(EX_BADARG);
527 530 }
528 531 call_pass = 1;
529 532 }
530 533
531 534 /* expiration string is a date, newer than today */
532 535 if (expire) {
533 536 if (*expire &&
534 537 valid_expire(expire, (time_t *)0) == INVALID) {
535 538 errmsg(M_INVALID, expire, "expiration date");
536 539 exit(EX_BADARG);
537 540 }
538 541 call_pass = 1;
539 542 }
540 543
541 544 if (nkeys > 0)
542 545 call_pass = 1;
543 546
544 547 /* that's it for validations - now do the work */
545 548
546 549 if (grps) {
547 550 /* redefine login's supplentary group memberships */
548 551 ret = edit_group(logname, new_logname, gidlist, 1);
549 552 if (ret != EX_SUCCESS) {
550 553 errmsg(M_UPDATE, "modified");
551 554 exit(ret);
552 555 }
553 556 }
554 557 if (projects) {
555 558 ret = edit_project(logname, (char *)NULL, projlist, 0);
556 559 if (ret != EX_SUCCESS) {
557 560 errmsg(M_UPDATE, "modified");
558 561 exit(ret);
559 562 }
560 563 }
561 564
562 565
563 566 if (!call_pass) exit(ret);
564 567
565 568 /* only get to here if need to call passmgmt */
566 569 /* set up arguments to passmgmt in nargv array */
567 570 nargv = malloc((30 + nkeys * 2) * sizeof (char *));
568 571
569 572 argindex = 0;
570 573 nargv[argindex++] = PASSMGMT;
571 574 nargv[argindex++] = "-m"; /* modify */
572 575
573 576 if (comment) { /* comment */
574 577 nargv[argindex++] = "-c";
575 578 nargv[argindex++] = comment;
576 579 }
577 580
578 581 if (dir) {
579 582 /* flags for home directory */
580 583 nargv[argindex++] = "-h";
581 584 nargv[argindex++] = dir;
582 585 }
583 586
584 587 if (group) {
585 588 /* set gid flag */
586 589 nargv[argindex++] = "-g";
587 590 (void) sprintf(gidstring, "%u", gid);
588 591 nargv[argindex++] = gidstring;
589 592 }
590 593
591 594 if (shell) { /* shell */
592 595 nargv[argindex++] = "-s";
593 596 nargv[argindex++] = shell;
594 597 }
595 598
596 599 if (inactstr) {
597 600 nargv[argindex++] = "-f";
598 601 nargv[argindex++] = inactstr;
599 602 }
600 603
601 604 if (expire) {
602 605 nargv[argindex++] = "-e";
603 606 nargv[argindex++] = expire;
604 607 }
605 608
606 609 if (uidstr) { /* set uid flag */
607 610 nargv[argindex++] = "-u";
608 611 (void) sprintf(uidstring, "%u", uid);
609 612 nargv[argindex++] = uidstring;
610 613 }
611 614
612 615 if (oflag) nargv[argindex++] = "-o";
613 616
614 617 if (new_logname) { /* redefine login name */
615 618 nargv[argindex++] = "-l";
616 619 nargv[argindex++] = new_logname;
617 620 }
618 621
619 622 if (nkeys > 0)
620 623 addkey_args(nargv, &argindex);
621 624
622 625 /* finally - login name */
623 626 nargv[argindex++] = logname;
624 627
625 628 /* set the last to null */
626 629 nargv[argindex++] = NULL;
627 630
628 631 /* now call passmgmt */
629 632 ret = PEX_FAILED;
630 633 for (tries = 3; ret != PEX_SUCCESS && tries--; ) {
631 634 switch (ret = call_passmgmt(nargv)) {
632 635 case PEX_SUCCESS:
633 636 case PEX_BUSY:
634 637 break;
635 638
636 639 case PEX_HOSED_FILES:
637 640 errmsg(M_HOSED_FILES);
638 641 exit(EX_INCONSISTENT);
639 642 break;
640 643
641 644 case PEX_SYNTAX:
642 645 case PEX_BADARG:
643 646 /* should NEVER occur that passmgmt usage is wrong */
644 647 if (is_role(usertype))
645 648 errmsg(M_MRUSAGE);
646 649 else
647 650 errmsg(M_MUSAGE);
648 651 exit(EX_SYNTAX);
649 652 break;
650 653
651 654 case PEX_BADUID:
652 655 /* uid in use - shouldn't happen print message anyway */
653 656 errmsg(M_UID_USED, uid);
654 657 exit(EX_ID_EXISTS);
655 658 break;
656 659
657 660 case PEX_BADNAME:
658 661 /* invalid loname */
659 662 errmsg(M_USED, logname);
660 663 exit(EX_NAME_EXISTS);
661 664 break;
662 665
663 666 default:
664 667 errmsg(M_UPDATE, "modified");
665 668 exit(ret);
666 669 break;
667 670 }
668 671 }
669 672 if (tries == 0) {
670 673 errmsg(M_UPDATE, "modified");
671 674 }
672 675
673 676 exit(ret);
674 677 /*NOTREACHED*/
675 678 }
↓ open down ↓ |
147 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX