Print this page
NEX-16805 Add smbutil discon command
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc.c
+++ new/usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc.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 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 24 */
25 25
26 26 /*
27 27 * SMBFS I/O Daemon (SMF service)
28 28 */
29 29
30 30 #include <sys/types.h>
31 31 #include <sys/stat.h>
32 32 #include <sys/note.h>
33 33 #include <sys/queue.h>
34 34
35 35 #include <errno.h>
36 36 #include <fcntl.h>
37 37 #include <signal.h>
38 38 #include <stdarg.h>
39 39 #include <stdio.h>
40 40 #include <string.h>
41 41 #include <strings.h>
42 42 #include <stdlib.h>
43 43 #include <synch.h>
44 44 #include <time.h>
45 45 #include <unistd.h>
46 46 #include <ucred.h>
47 47 #include <wait.h>
48 48 #include <priv_utils.h>
49 49 #include <err.h>
50 50 #include <door.h>
51 51 #include <libscf.h>
52 52 #include <locale.h>
53 53 #include <thread.h>
54 54 #include <assert.h>
55 55
56 56 #include <netsmb/smb_lib.h>
57 57
58 58 static boolean_t d_flag = B_FALSE;
59 59
60 60 /* Keep a list of child processes. */
61 61 typedef struct _child {
62 62 LIST_ENTRY(_child) list;
63 63 pid_t pid;
64 64 uid_t uid;
65 65 } child_t;
66 66 static LIST_HEAD(, _child) child_list = { 0 };
67 67 mutex_t cl_mutex = DEFAULTMUTEX;
68 68
69 69 static const char smbiod_path[] = "/usr/lib/smbfs/smbiod";
70 70 static const char door_path[] = SMBIOD_SVC_DOOR;
71 71
72 72 void svc_dispatch(void *cookie, char *argp, size_t argsz,
73 73 door_desc_t *dp, uint_t n_desc);
74 74 static int cmd_start(uid_t uid, gid_t gid);
75 75 static int new_child(uid_t uid, gid_t gid);
76 76 static void svc_sigchld(void);
77 77 static void child_gone(uid_t, pid_t, int);
78 78 static void svc_cleanup(void);
79 79
80 80 static child_t *
81 81 child_find_by_pid(pid_t pid)
82 82 {
83 83 child_t *cp;
84 84
85 85 assert(MUTEX_HELD(&cl_mutex));
86 86 LIST_FOREACH(cp, &child_list, list) {
87 87 if (cp->pid == pid)
88 88 return (cp);
89 89 }
90 90 return (NULL);
91 91 }
92 92
93 93 static child_t *
94 94 child_find_by_uid(uid_t uid)
95 95 {
96 96 child_t *cp;
97 97
98 98 assert(MUTEX_HELD(&cl_mutex));
99 99 LIST_FOREACH(cp, &child_list, list) {
100 100 if (cp->uid == uid)
101 101 return (cp);
102 102 }
103 103 return (NULL);
104 104 }
105 105
106 106 /*
107 107 * Find out if the service is already running.
108 108 * Return: true, false.
109 109 */
110 110 static boolean_t
111 111 already_running(void)
112 112 {
113 113 door_info_t info;
114 114 int fd, rc;
115 115
116 116 if ((fd = open(door_path, O_RDONLY)) < 0)
117 117 return (B_FALSE);
118 118
119 119 rc = door_info(fd, &info);
120 120 close(fd);
121 121 if (rc < 0)
122 122 return (B_FALSE);
123 123
124 124 return (B_TRUE);
125 125 }
126 126
127 127 /*
128 128 * This function will fork off a child process,
129 129 * from which only the child will return.
130 130 *
131 131 * The parent exit status is taken as the SMF start method
132 132 * success or failure, so the parent waits (via pipe read)
133 133 * for the child to finish initialization before it exits.
134 134 * Use SMF error codes only on exit.
135 135 */
136 136 static int
137 137 daemonize_init(void)
138 138 {
139 139 int pid, st;
140 140 int pfds[2];
141 141
142 142 chdir("/");
143 143
144 144 if (pipe(pfds) < 0) {
145 145 perror("pipe");
146 146 exit(SMF_EXIT_ERR_FATAL);
147 147 }
148 148 if ((pid = fork1()) == -1) {
149 149 perror("fork");
150 150 exit(SMF_EXIT_ERR_FATAL);
151 151 }
152 152
153 153 /*
154 154 * If we're the parent process, wait for either the child to send us
155 155 * the appropriate exit status over the pipe or for the read to fail
156 156 * (presumably with 0 for EOF if our child terminated abnormally).
157 157 * If the read fails, exit with either the child's exit status if it
158 158 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
159 159 */
160 160 if (pid != 0) {
161 161 /* parent */
162 162 close(pfds[1]);
163 163 if (read(pfds[0], &st, sizeof (st)) == sizeof (st))
164 164 _exit(st);
165 165 if (waitpid(pid, &st, 0) == pid && WIFEXITED(st))
166 166 _exit(WEXITSTATUS(st));
167 167 _exit(SMF_EXIT_ERR_FATAL);
168 168 }
169 169
170 170 /* child */
171 171 close(pfds[0]);
172 172
173 173 return (pfds[1]);
174 174 }
175 175
176 176 static void
177 177 daemonize_fini(int pfd, int rc)
178 178 {
179 179 /* Tell parent we're ready. */
180 180 (void) write(pfd, &rc, sizeof (rc));
181 181 close(pfd);
182 182 }
183 183
184 184 int
185 185 main(int argc, char **argv)
186 186 {
187 187 sigset_t oldmask, tmpmask;
188 188 struct sigaction sa;
189 189 struct rlimit rl;
190 190 int door_fd = -1, tmp_fd = -1, pfd = -1;
191 191 int c, sig;
192 192 int rc = SMF_EXIT_ERR_FATAL;
193 193 boolean_t created = B_FALSE, attached = B_FALSE;
194 194
195 195 /* set locale and text domain for i18n */
196 196 (void) setlocale(LC_ALL, "");
197 197 (void) textdomain(TEXT_DOMAIN);
198 198
199 199 while ((c = getopt(argc, argv, "d")) != -1) {
200 200 switch (c) {
201 201 case 'd':
202 202 /* Do debug messages. */
203 203 d_flag = B_TRUE;
204 204 break;
205 205 default:
206 206 fprintf(stderr, "Usage: %s [-d]\n", argv[0]);
207 207 return (SMF_EXIT_ERR_CONFIG);
208 208 }
209 209 }
210 210
211 211 if (already_running()) {
212 212 fprintf(stderr, "%s: already running", argv[0]);
213 213 return (rc);
214 214 }
215 215
216 216 /*
217 217 * Raise the fd limit to max
218 218 * errors here are non-fatal
219 219 */
220 220 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
221 221 fprintf(stderr, "getrlimit failed, err %d\n", errno);
222 222 } else if (rl.rlim_cur < rl.rlim_max) {
223 223 rl.rlim_cur = rl.rlim_max;
224 224 if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
225 225 fprintf(stderr, "setrlimit "
226 226 "RLIMIT_NOFILE %d, err %d",
227 227 (int)rl.rlim_cur, errno);
228 228 }
229 229
230 230 /*
231 231 * Want all signals blocked, as we're doing
232 232 * synchronous delivery via sigwait below.
233 233 */
234 234 sigfillset(&tmpmask);
235 235 sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
236 236
237 237 /*
238 238 * Do want SIGCHLD, and will waitpid().
239 239 */
240 240 sa.sa_flags = SA_NOCLDSTOP;
241 241 sa.sa_handler = SIG_DFL;
242 242 sigemptyset(&sa.sa_mask);
243 243 sigaction(SIGCHLD, &sa, NULL);
244 244
245 245 /*
246 246 * Daemonize, unless debugging.
247 247 */
248 248 if (d_flag) {
249 249 /* debug: run in foregound (not a service) */
250 250 putenv("SMBFS_DEBUG=1");
251 251 } else {
252 252 /* Non-debug: start daemon in the background. */
253 253 pfd = daemonize_init();
254 254 }
255 255
256 256 /*
257 257 * Create directory for all smbiod doors.
258 258 */
259 259 if ((mkdir(SMBIOD_RUNDIR, 0755) < 0) && errno != EEXIST) {
260 260 perror(SMBIOD_RUNDIR);
261 261 goto out;
262 262 }
263 263
264 264 /*
265 265 * Create a file for the main service door.
266 266 */
267 267 unlink(door_path);
268 268 tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0644);
269 269 if (tmp_fd < 0) {
270 270 perror(door_path);
271 271 goto out;
272 272 }
273 273 close(tmp_fd);
274 274 tmp_fd = -1;
275 275 created = B_TRUE;
276 276
277 277 /* Setup the door service. */
278 278 door_fd = door_create(svc_dispatch, NULL,
279 279 DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
280 280 if (door_fd == -1) {
281 281 perror("svc door_create");
282 282 goto out;
283 283 }
284 284 fdetach(door_path);
285 285 if (fattach(door_fd, door_path) < 0) {
286 286 fprintf(stderr, "%s: fattach failed, %s\n",
287 287 door_path, strerror(errno));
288 288 goto out;
289 289 }
290 290 attached = B_TRUE;
291 291
292 292 /*
293 293 * Initializations done. Tell start method we're up.
294 294 */
295 295 rc = SMF_EXIT_OK;
296 296 if (pfd != -1) {
297 297 daemonize_fini(pfd, rc);
298 298 pfd = -1;
299 299 }
300 300
301 301 /*
302 302 * Main thread just waits for signals.
303 303 */
304 304 again:
305 305 sig = sigwait(&tmpmask);
306 306 if (d_flag)
307 307 fprintf(stderr, "main: sig=%d\n", sig);
308 308 switch (sig) {
309 309 case SIGINT:
310 310 case SIGTERM:
311 311 /*
312 312 * The whole process contract gets a SIGTERM
313 313 * at once. Give children a chance to exit
314 314 * so we can do normal SIGCHLD cleanup.
315 315 * Prevent new door_open calls.
316 316 */
317 317 fdetach(door_path);
318 318 attached = B_FALSE;
319 319 alarm(2);
320 320 goto again;
321 321 case SIGALRM:
322 322 break; /* normal termination */
323 323 case SIGCHLD:
324 324 svc_sigchld();
325 325 goto again;
326 326 case SIGCONT:
327 327 goto again;
328 328 default:
329 329 /* Unexpected signal. */
330 330 fprintf(stderr, "svc_main: unexpected sig=%d\n", sig);
331 331 break;
332 332 }
333 333
334 334 out:
335 335 if (attached)
336 336 fdetach(door_path);
337 337 if (door_fd != -1)
338 338 door_revoke(door_fd);
339 339 if (created)
340 340 unlink(door_path);
341 341
342 342 /* NB: door threads gone now. */
343 343 svc_cleanup();
344 344
345 345 /* If startup error, report to parent. */
346 346 if (pfd != -1)
347 347 daemonize_fini(pfd, rc);
348 348
349 349 return (rc);
350 350 }
351 351
352 352 /*ARGSUSED*/
353 353 void
354 354 svc_dispatch(void *cookie, char *argp, size_t argsz,
355 355 door_desc_t *dp, uint_t n_desc)
356 356 {
357 357 ucred_t *ucred = NULL;
358 358 uid_t uid;
359 359 gid_t gid;
360 360 int32_t cmd, rc;
361 361
362 362 /*
363 363 * Allow a NULL arg call to check if this
364 364 * daemon is running. Just return zero.
365 365 */
366 366 if (argp == NULL) {
367 367 rc = 0;
368 368 goto out;
369 369 }
370 370
371 371 /*
372 372 * Get the caller's credentials.
373 373 * (from client side of door)
374 374 */
375 375 if (door_ucred(&ucred) != 0) {
376 376 rc = EACCES;
377 377 goto out;
378 378 }
379 379 uid = ucred_getruid(ucred);
380 380 gid = ucred_getrgid(ucred);
381 381
382 382 /*
383 383 * Arg is just an int command code.
384 384 * Reply is also an int.
385 385 */
386 386 if (argsz != sizeof (cmd)) {
387 387 rc = EINVAL;
388 388 goto out;
389 389 }
390 390 bcopy(argp, &cmd, sizeof (cmd));
391 391 switch (cmd) {
392 392 case SMBIOD_START:
393 393 rc = cmd_start(uid, gid);
394 394 break;
395 395 default:
396 396 rc = EINVAL;
397 397 goto out;
398 398 }
399 399
400 400 out:
401 401 if (ucred != NULL)
402 402 ucred_free(ucred);
403 403
404 404 door_return((void *)&rc, sizeof (rc), NULL, 0);
405 405 }
406 406
407 407 /*
408 408 * Start a per-user smbiod, if not already running.
409 409 */
410 410 int
411 411 cmd_start(uid_t uid, gid_t gid)
412 412 {
413 413 char door_file[64];
414 414 child_t *cp;
415 415 int pid, fd = -1;
416 416
417 417 mutex_lock(&cl_mutex);
418 418 cp = child_find_by_uid(uid);
419 419 if (cp != NULL) {
420 420 /* This UID already has an IOD. */
421 421 mutex_unlock(&cl_mutex);
422 422 if (d_flag) {
423 423 fprintf(stderr, "cmd_start: uid %d"
424 424 " already has an iod\n", uid);
425 425 }
426 426 return (0);
427 427 }
428 428
429 429 /*
430 430 * OK, create a new child.
431 431 */
432 432 cp = malloc(sizeof (*cp));
433 433 if (cp == NULL) {
434 434 mutex_unlock(&cl_mutex);
435 435 return (ENOMEM);
436 436 }
437 437 cp->pid = 0; /* update below */
438 438 cp->uid = uid;
439 439 LIST_INSERT_HEAD(&child_list, cp, list);
440 440 mutex_unlock(&cl_mutex);
441 441
442 442 /*
443 443 * The child will not have permission to create or
444 444 * destroy files in SMBIOD_RUNDIR so do that here.
445 445 */
446 446 snprintf(door_file, sizeof (door_file),
447 447 SMBIOD_USR_DOOR, cp->uid);
448 448 unlink(door_file);
449 449 fd = open(door_file, O_RDWR|O_CREAT|O_EXCL, 0600);
450 450 if (fd < 0) {
451 451 perror(door_file);
452 452 goto errout;
453 453 }
454 454 if (fchown(fd, uid, gid) < 0) {
455 455 perror(door_file);
456 456 goto errout;
457 457 }
458 458 close(fd);
459 459 fd = -1;
460 460
461 461 if ((pid = fork1()) == -1) {
462 462 perror("fork");
463 463 goto errout;
464 464 }
465 465 if (pid == 0) {
466 466 (void) new_child(uid, gid);
467 467 _exit(1);
468 468 }
469 469 /* parent */
470 470 cp->pid = pid;
471 471
472 472 if (d_flag) {
473 473 fprintf(stderr, "cmd_start: uid %d new iod, pid %d\n",
474 474 uid, pid);
475 475 }
476 476
477 477 return (0);
478 478
479 479 errout:
480 480 if (fd != -1)
481 481 close(fd);
482 482 mutex_lock(&cl_mutex);
483 483 LIST_REMOVE(cp, list);
484 484 mutex_unlock(&cl_mutex);
485 485 free(cp);
486 486 return (errno);
487 487 }
488 488
489 489 /*
490 490 * Assume the passed credentials (from the door client),
491 491 * drop any extra privileges, and exec the per-user iod.
492 492 */
493 493 static int
494 494 new_child(uid_t uid, gid_t gid)
495 495 {
496 496 char *argv[2];
497 497 int flags, rc;
498 498
499 499 flags = PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS;
500 500 rc = __init_daemon_priv(flags, uid, gid, PRIV_NET_ACCESS, NULL);
501 501 if (rc != 0)
502 502 return (errno);
503 503
504 504 argv[0] = "smbiod";
505 505 argv[1] = NULL;
506 506 (void) execv(smbiod_path, argv);
507 507 return (errno);
508 508 }
509 509
510 510 static void
511 511 svc_sigchld(void)
512 512 {
513 513 child_t *cp;
514 514 pid_t pid;
515 515 int err, status, found = 0;
516 516
517 517 mutex_lock(&cl_mutex);
518 518
519 519 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
520 520
521 521 found++;
522 522 if (d_flag)
523 523 fprintf(stderr, "svc_sigchld: pid %d\n", (int)pid);
524 524
525 525 cp = child_find_by_pid(pid);
526 526 if (cp == NULL) {
527 527 fprintf(stderr, "Unknown pid %d\n", (int)pid);
528 528 continue;
529 529 }
530 530 child_gone(cp->uid, cp->pid, status);
531 531 LIST_REMOVE(cp, list);
532 532 free(cp);
533 533 }
534 534 err = errno;
535 535
536 536 mutex_unlock(&cl_mutex);
537 537
538 538 /* ECHILD is the normal end of loop. */
539 539 if (pid < 0 && err != ECHILD)
540 540 fprintf(stderr, "svc_sigchld: waitpid err %d\n", err);
541 541 if (found == 0)
542 542 fprintf(stderr, "svc_sigchld: no children?\n");
543 543 }
544 544
545 545 static void
546 546 child_gone(uid_t uid, pid_t pid, int status)
547 547 {
548 548 char door_file[64];
549 549 int x;
550 550
551 551 if (d_flag)
552 552 fprintf(stderr, "child_gone: uid %d pid %d\n",
|
↓ open down ↓ |
552 lines elided |
↑ open up ↑ |
553 553 uid, (int)pid);
554 554
555 555 snprintf(door_file, sizeof (door_file),
556 556 SMBIOD_RUNDIR "/%d", uid);
557 557 unlink(door_file);
558 558
559 559 if (WIFEXITED(status)) {
560 560 x = WEXITSTATUS(status);
561 561 if (x != 0) {
562 562 fprintf(stderr,
563 - "uid %d, pid %d exit %d",
563 + "uid %d, pid %d exit %d\n",
564 564 uid, (int)pid, x);
565 565 }
566 566 }
567 567 if (WIFSIGNALED(status)) {
568 568 x = WTERMSIG(status);
569 569 fprintf(stderr,
570 - "uid %d, pid %d signal %d",
570 + "uid %d, pid %d signal %d\n",
571 571 uid, (int)pid, x);
572 572 }
573 573 }
574 574
575 575 /*
576 576 * Final cleanup before exit. Unlink child doors, etc.
577 577 * Called while single threaded, so no locks needed here.
578 578 * The list is normally empty by now due to svc_sigchld
579 579 * calls during shutdown. But in case there were any
580 580 * straglers, do cleanup here. Don't bother freeing any
581 581 * list elements here, as we're exiting.
582 582 */
583 583 static void
584 584 svc_cleanup(void)
585 585 {
586 586 child_t *cp;
587 587
588 588 LIST_FOREACH(cp, &child_list, list) {
589 589 child_gone(cp->uid, cp->pid, 0);
590 590 }
591 591 }
|
↓ open down ↓ |
11 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX