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 /*
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Joyent, Inc.
25 */
26
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <syslog.h>
36 #include <zone.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <stropts.h>
40 #include <sys/conf.h>
41 #include <pthread.h>
42 #include <unistd.h>
43 #include <wait.h>
44 #include <libcontract.h>
45 #include <libcontract_priv.h>
46 #include <sys/contract/process.h>
47 #include <zone.h>
48 #include "dlmgmt_impl.h"
49
50 typedef enum dlmgmt_db_op {
51 DLMGMT_DB_OP_WRITE,
52 DLMGMT_DB_OP_DELETE,
53 DLMGMT_DB_OP_READ
54 } dlmgmt_db_op_t;
55
56 typedef struct dlmgmt_db_req_s {
57 struct dlmgmt_db_req_s *ls_next;
58 dlmgmt_db_op_t ls_op;
59 char ls_link[MAXLINKNAMELEN];
60 datalink_id_t ls_linkid;
61 zoneid_t ls_zoneid;
62 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */
63 /* DLMGMT_PERSIST, not both. */
64 } dlmgmt_db_req_t;
65
66 /*
67 * List of pending db updates (e.g., because of a read-only filesystem).
68 */
69 static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL;
70 static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL;
71
72 /*
73 * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
74 * line with an old format. This will cause the file being read to be
75 * re-written with the current format.
76 */
77 static boolean_t rewrite_needed;
78
79 static int dlmgmt_db_update(dlmgmt_db_op_t, const char *,
80 dlmgmt_link_t *, uint32_t);
81 static int dlmgmt_process_db_req(dlmgmt_db_req_t *);
82 static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
83 static void *dlmgmt_db_update_thread(void *);
84 static boolean_t process_link_line(char *, dlmgmt_link_t *);
85 static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
86 static int process_db_read(dlmgmt_db_req_t *, FILE *);
87 static void generate_link_line(dlmgmt_link_t *, boolean_t, char *);
88
89 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
90 #define MAXLINELEN 1024
91
92 typedef void db_walk_func_t(dlmgmt_link_t *);
93
94 /*
95 * Translator functions to go from dladm_datatype_t to character strings.
96 * Each function takes a pointer to a buffer, the size of the buffer,
97 * the name of the attribute, and the value to be written. The functions
98 * return the number of bytes written to the buffer. If the buffer is not big
99 * enough to hold the string representing the value, then nothing is written
100 * and 0 is returned.
101 */
102 typedef size_t write_func_t(char *, size_t, char *, void *);
103
104 /*
105 * Translator functions to read from a NULL terminated string buffer into
106 * something of the given DLADM_TYPE_*. The functions each return the number
107 * of bytes read from the string buffer. If there is an error reading data
108 * from the buffer, then 0 is returned. It is the caller's responsibility
109 * to free the data allocated by these functions.
110 */
111 typedef size_t read_func_t(char *, void **);
112
113 typedef struct translator_s {
114 const char *type_name;
115 write_func_t *write_func;
116 read_func_t *read_func;
117 } translator_t;
118
119 /*
120 * Translator functions, defined later but declared here so that
121 * the translator table can be defined.
122 */
123 static write_func_t write_str, write_boolean, write_uint64;
124 static read_func_t read_str, read_boolean, read_int64;
125
126 /*
127 * Translator table, indexed by dladm_datatype_t.
128 */
129 static translator_t translators[] = {
130 { "string", write_str, read_str },
131 { "boolean", write_boolean, read_boolean },
132 { "int", write_uint64, read_int64 }
133 };
134
135 static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
136
137 #define LINK_PROPERTY_DELIMINATOR ";"
138 #define LINK_PROPERTY_TYPE_VALUE_SEP ","
139 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
140 strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
141 strlen(LINK_PROPERTY_DELIMINATOR) +\
142 strlen((n)))
143 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
144 (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
145 translators[(type)].type_name, \
146 LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
147
148 /*
149 * Name of the cache file to keep the active <link name, linkid> mapping
150 */
151 char cachefile[MAXPATHLEN];
152
153 #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf"
154 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \
155 (void) snprintf((buffer), MAXPATHLEN, "%s", \
156 (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
157
158 typedef struct zopen_arg {
159 const char *zopen_modestr;
160 int *zopen_pipe;
161 int zopen_fd;
162 } zopen_arg_t;
163
164 typedef struct zrename_arg {
165 const char *zrename_newname;
166 } zrename_arg_t;
167
168 typedef union zfoparg {
169 zopen_arg_t zfop_openarg;
170 zrename_arg_t zfop_renamearg;
171 } zfoparg_t;
172
173 typedef struct zfcbarg {
174 boolean_t zfarg_inglobalzone; /* is callback in global zone? */
175 zoneid_t zfarg_finglobalzone; /* is file in global zone? */
176 const char *zfarg_filename;
177 zfoparg_t *zfarg_oparg;
178 } zfarg_t;
179 #define zfarg_openarg zfarg_oparg->zfop_openarg
180 #define zfarg_renamearg zfarg_oparg->zfop_renamearg
181
182 /* zone file callback */
183 typedef int zfcb_t(zfarg_t *);
184
185 /*
186 * Execute an operation on filename relative to zoneid's zone root. If the
187 * file is in the global zone, then the zfcb() callback will simply be called
188 * directly. If the file is in a non-global zone, then zfcb() will be called
189 * both from the global zone's context, and from the non-global zone's context
190 * (from a fork()'ed child that has entered the non-global zone). This is
191 * done to allow the callback to communicate with itself if needed (e.g. to
192 * pass back the file descriptor of an opened file).
193 */
194 static int
195 dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
196 zfoparg_t *zfoparg)
197 {
198 int ctfd;
199 int err;
200 pid_t childpid;
201 siginfo_t info;
202 zfarg_t zfarg;
203 ctid_t ct;
204
205 if (zoneid != GLOBAL_ZONEID) {
206 /*
207 * We need to access a file that isn't in the global zone.
208 * Accessing non-global zone files from the global zone is
209 * unsafe (due to symlink attacks), we'll need to fork a child
210 * that enters the zone in question and executes the callback
211 * that will operate on the file.
212 *
213 * Before we proceed with this zone tango, we need to create a
214 * new process contract for the child, as required by
215 * zone_enter().
216 */
217 errno = 0;
218 ctfd = open64("/system/contract/process/template", O_RDWR);
219 if (ctfd == -1)
220 return (errno);
221 if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
222 (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
223 (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
224 (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
225 (err = ct_tmpl_activate(ctfd)) != 0) {
226 (void) close(ctfd);
227 return (err);
228 }
229 childpid = fork();
230 switch (childpid) {
231 case -1:
232 (void) ct_tmpl_clear(ctfd);
233 (void) close(ctfd);
234 return (err);
235 case 0:
236 (void) ct_tmpl_clear(ctfd);
237 (void) close(ctfd);
238 /*
239 * Elevate our privileges as zone_enter() requires all
240 * privileges.
241 */
242 if ((err = dlmgmt_elevate_privileges()) != 0)
243 _exit(err);
244 if (zone_enter(zoneid) == -1)
245 _exit(errno);
246 if ((err = dlmgmt_drop_privileges()) != 0)
247 _exit(err);
248 break;
249 default:
250 if (contract_latest(&ct) == -1)
251 ct = -1;
252 (void) ct_tmpl_clear(ctfd);
253 (void) close(ctfd);
254 if (waitid(P_PID, childpid, &info, WEXITED) == -1) {
255 (void) contract_abandon_id(ct);
256 return (errno);
257 }
258 (void) contract_abandon_id(ct);
259 if (info.si_status != 0)
260 return (info.si_status);
261 }
262 }
263
264 zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
265 zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
266 zfarg.zfarg_filename = filename;
267 zfarg.zfarg_oparg = zfoparg;
268 err = zfcb(&zfarg);
269 if (!zfarg.zfarg_inglobalzone)
270 _exit(err);
271 return (err);
272 }
273
274 static int
275 dlmgmt_zopen_cb(zfarg_t *zfarg)
276 {
277 struct strrecvfd recvfd;
278 boolean_t newfile = B_FALSE;
279 boolean_t inglobalzone = zfarg->zfarg_inglobalzone;
280 zoneid_t finglobalzone = zfarg->zfarg_finglobalzone;
281 const char *filename = zfarg->zfarg_filename;
282 const char *modestr = zfarg->zfarg_openarg.zopen_modestr;
283 int *p = zfarg->zfarg_openarg.zopen_pipe;
284 struct stat statbuf;
285 int oflags;
286 mode_t mode;
287 int fd = -1;
288 int err;
289
290 /* We only ever open a file for reading or writing, not both. */
291 oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
292 mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
293
294 /* Open the file if we're in the same zone as the file. */
295 if (inglobalzone == finglobalzone) {
296 /*
297 * First determine if we will be creating the file as part of
298 * opening it. If so, then we'll need to ensure that it has
299 * the proper ownership after having opened it.
300 */
301 if (oflags & O_CREAT) {
302 if (stat(filename, &statbuf) == -1) {
303 if (errno == ENOENT)
304 newfile = B_TRUE;
305 else
306 return (errno);
307 }
308 }
309 if ((fd = open(filename, oflags, mode)) == -1)
310 return (errno);
311 if (newfile) {
312 if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
313 err = errno;
314 (void) close(fd);
315 return (err);
316 }
317 }
318 }
319
320 /*
321 * If we're not in the global zone, send the file-descriptor back to
322 * our parent in the global zone.
323 */
324 if (!inglobalzone) {
325 assert(!finglobalzone);
326 assert(fd != -1);
327 return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
328 }
329
330 /*
331 * At this point, we know we're in the global zone. If the file was
332 * in a non-global zone, receive the file-descriptor from our child in
333 * the non-global zone.
334 */
335 if (!finglobalzone) {
336 if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
337 return (errno);
338 fd = recvfd.fd;
339 }
340
341 zfarg->zfarg_openarg.zopen_fd = fd;
342 return (0);
343 }
344
345 static int
346 dlmgmt_zunlink_cb(zfarg_t *zfarg)
347 {
348 if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
349 return (0);
350 return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
351 }
352
353 static int
354 dlmgmt_zrename_cb(zfarg_t *zfarg)
355 {
356 if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
357 return (0);
358 return (rename(zfarg->zfarg_filename,
359 zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
360 }
361
362 /*
363 * Same as fopen(3C), except that it opens the file relative to zoneid's zone
364 * root.
365 */
366 static FILE *
367 dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
368 int *err)
369 {
370 int p[2];
371 zfoparg_t zfoparg;
372 FILE *fp = NULL;
373
374 if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
375 *err = errno;
376 return (NULL);
377 }
378
379 zfoparg.zfop_openarg.zopen_modestr = modestr;
380 zfoparg.zfop_openarg.zopen_pipe = p;
381 *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
382 if (zoneid != GLOBAL_ZONEID) {
383 (void) close(p[0]);
384 (void) close(p[1]);
385 }
386 if (*err == 0) {
387 fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
388 if (fp == NULL) {
389 *err = errno;
390 (void) close(zfoparg.zfop_openarg.zopen_fd);
391 }
392 }
393 return (fp);
394 }
395
396 /*
397 * Same as rename(2), except that old and new are relative to zoneid's zone
398 * root.
399 */
400 static int
401 dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
402 {
403 zfoparg_t zfoparg;
404
405 zfoparg.zfop_renamearg.zrename_newname = new;
406 return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
407 }
408
409 /*
410 * Same as unlink(2), except that filename is relative to zoneid's zone root.
411 */
412 static int
413 dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
414 {
415 return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
416 }
417
418 static size_t
419 write_str(char *buffer, size_t buffer_length, char *name, void *value)
420 {
421 char *ptr = value;
422 size_t data_length = strnlen(ptr, buffer_length);
423
424 /*
425 * Strings are assumed to be NULL terminated. In order to fit in
426 * the buffer, the string's length must be less then buffer_length.
427 * If the value is empty, there's no point in writing it, in fact,
428 * we shouldn't even see that case.
429 */
430 if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
431 buffer_length || data_length == 0)
432 return (0);
433
434 /*
435 * Since we know the string will fit in the buffer, snprintf will
436 * always return less than buffer_length, so we can just return
437 * whatever snprintf returns.
438 */
439 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
440 name, DLADM_TYPE_STR, ptr));
441 }
442
443 static size_t
444 write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
445 {
446 boolean_t *ptr = value;
447
448 /*
449 * Booleans are either zero or one, so we only need room for two
450 * characters in the buffer.
451 */
452 if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
453 return (0);
454
455 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
456 name, DLADM_TYPE_BOOLEAN, *ptr));
457 }
458
459 static size_t
460 write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
461 {
462 uint64_t *ptr = value;
463
464 /*
465 * Limit checking for uint64_t is a little trickier.
466 */
467 if (snprintf(NULL, 0, "%lld", *ptr) +
468 BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
469 return (0);
470
471 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
472 name, DLADM_TYPE_UINT64, *ptr));
473 }
474
475 static size_t
476 read_str(char *buffer, void **value)
477 {
478 char *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
479 ssize_t len;
480
481 if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
482 >= MAXLINKATTRVALLEN) {
483 free(ptr);
484 return (0);
485 }
486
487 *(char **)value = ptr;
488
489 /* Account for NULL terminator */
490 return (len + 1);
491 }
492
493 static size_t
494 read_boolean(char *buffer, void **value)
495 {
496 boolean_t *ptr = calloc(1, sizeof (boolean_t));
497
498 if (ptr == NULL)
499 return (0);
500
501 *ptr = atoi(buffer);
502 *(boolean_t **)value = ptr;
503
504 return (sizeof (boolean_t));
505 }
506
507 static size_t
508 read_int64(char *buffer, void **value)
509 {
510 int64_t *ptr = calloc(1, sizeof (int64_t));
511
512 if (ptr == NULL)
513 return (0);
514
515 *ptr = (int64_t)atoll(buffer);
516 *(int64_t **)value = ptr;
517
518 return (sizeof (int64_t));
519 }
520
521 static dlmgmt_db_req_t *
522 dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
523 datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
524 {
525 dlmgmt_db_req_t *req;
526
527 if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
528 *err = errno;
529 } else {
530 req->ls_op = op;
531 if (linkname != NULL)
532 (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
533 req->ls_linkid = linkid;
534 req->ls_zoneid = zoneid;
535 req->ls_flags = flags;
536 }
537 return (req);
538 }
539
540 /*
541 * Update the db entry with name "entryname" using information from "linkp".
542 */
543 static int
544 dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
545 uint32_t flags)
546 {
547 dlmgmt_db_req_t *req;
548 int err;
549
550 /* It is either a persistent request or an active request, not both. */
551 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
552
553 if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
554 linkp->ll_zoneid, flags, &err)) == NULL)
555 return (err);
556
557 /* If transient op and onloan, use the global zone cache file. */
558 if (flags == DLMGMT_ACTIVE && linkp->ll_onloan)
559 req->ls_zoneid = GLOBAL_ZONEID;
560
561 /*
562 * If the return error is EINPROGRESS, this request is handled
563 * asynchronously; return success.
564 */
565 err = dlmgmt_process_db_req(req);
566 if (err != EINPROGRESS)
567 free(req);
568 else
569 err = 0;
570 return (err);
571 }
572
573 #define DLMGMT_DB_OP_STR(op) \
574 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
575 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
576
577 #define DLMGMT_DB_CONF_STR(flag) \
578 (((flag) == DLMGMT_ACTIVE) ? "active" : \
579 (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
580
581 static int
582 dlmgmt_process_db_req(dlmgmt_db_req_t *req)
583 {
584 pthread_t tid;
585 boolean_t writeop;
586 int err;
587
588 /*
589 * If there are already pending "write" requests, queue this request in
590 * the pending list. Note that this function is called while the
591 * dlmgmt_rw_lock is held, so it is safe to access the global variables.
592 */
593 writeop = (req->ls_op != DLMGMT_DB_OP_READ);
594 if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
595 (dlmgmt_db_req_head != NULL)) {
596 dlmgmt_db_req_tail->ls_next = req;
597 dlmgmt_db_req_tail = req;
598 return (EINPROGRESS);
599 }
600
601 err = dlmgmt_process_db_onereq(req, writeop);
602 if (err != EINPROGRESS && err != 0 && err != ENOENT) {
603 /*
604 * Log the error unless the request processing is still in
605 * progress or if the configuration file hasn't been created
606 * yet (ENOENT).
607 */
608 dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
609 "operation on %s configuration failed: %s",
610 DLMGMT_DB_OP_STR(req->ls_op),
611 DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
612 }
613
614 if (err == EINPROGRESS) {
615 assert(req->ls_flags == DLMGMT_PERSIST);
616 assert(writeop && dlmgmt_db_req_head == NULL);
617 dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
618 err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
619 if (err == 0)
620 return (EINPROGRESS);
621 }
622 return (err);
623 }
624
625 static int
626 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
627 {
628 int err = 0;
629 FILE *fp, *nfp = NULL;
630 char file[MAXPATHLEN];
631 char newfile[MAXPATHLEN];
632
633 DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
634 fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
635 /*
636 * Note that it is not an error if the file doesn't exist. If we're
637 * reading, we treat this case the same way as an empty file. If
638 * we're writing, the file will be created when we open the file for
639 * writing below.
640 */
641 if (fp == NULL && !writeop)
642 return (err);
643
644 if (writeop) {
645 (void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
646 nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
647 if (nfp == NULL) {
648 /*
649 * EROFS can happen at boot when the file system is
650 * read-only. Return EINPROGRESS so that the caller
651 * can add this request to the pending request list
652 * and start a retry thread.
653 */
654 err = (errno == EROFS ? EINPROGRESS : errno);
655 goto done;
656 }
657 }
658 if (writeop) {
659 if ((err = process_db_write(req, fp, nfp)) == 0)
660 err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
661 } else {
662 err = process_db_read(req, fp);
663 }
664
665 done:
666 if (nfp != NULL) {
667 (void) fclose(nfp);
668 if (err != 0)
669 (void) dlmgmt_zunlink(newfile, req->ls_zoneid);
670 }
671 (void) fclose(fp);
672 return (err);
673 }
674
675 /*ARGSUSED*/
676 static void *
677 dlmgmt_db_update_thread(void *arg)
678 {
679 dlmgmt_db_req_t *req;
680
681 dlmgmt_table_lock(B_TRUE);
682
683 assert(dlmgmt_db_req_head != NULL);
684 while ((req = dlmgmt_db_req_head) != NULL) {
685 assert(req->ls_flags == DLMGMT_PERSIST);
686 if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
687 /*
688 * The filesystem is still read only. Go to sleep and
689 * try again.
690 */
691 dlmgmt_table_unlock();
692 (void) sleep(5);
693 dlmgmt_table_lock(B_TRUE);
694 continue;
695 }
696
697 /*
698 * The filesystem is no longer read only. Continue processing
699 * and remove the request from the pending list.
700 */
701 dlmgmt_db_req_head = req->ls_next;
702 if (dlmgmt_db_req_tail == req) {
703 assert(dlmgmt_db_req_head == NULL);
704 dlmgmt_db_req_tail = NULL;
705 }
706 free(req);
707 }
708
709 dlmgmt_table_unlock();
710 return (NULL);
711 }
712
713 static int
714 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
715 {
716 boolean_t found_type = B_FALSE;
717 dladm_datatype_t type = DLADM_TYPE_STR;
718 int i, len;
719 char *curr;
720 char attr_name[MAXLINKATTRLEN];
721 size_t attr_buf_len = 0;
722 void *attr_buf = NULL;
723 boolean_t rename;
724
725 curr = buf;
726 len = strlen(buf);
727 attr_name[0] = '\0';
728 for (i = 0; i < len; i++) {
729 rename = B_FALSE;
730 char c = buf[i];
731 boolean_t match = (c == '=' ||
732 (c == ',' && !found_type) || c == ';');
733
734 /*
735 * Move to the next character if there is no match and
736 * if we have not reached the last character.
737 */
738 if (!match && i != len - 1)
739 continue;
740
741 if (match) {
742 /*
743 * NUL-terminate the string pointed to by 'curr'.
744 */
745 buf[i] = '\0';
746 if (*curr == '\0')
747 goto parse_fail;
748 }
749
750 if (attr_name[0] != '\0' && found_type) {
751 /*
752 * We get here after we have processed the "<prop>="
753 * pattern. The pattern we are now interested in is
754 * "<val>;".
755 */
756 if (c == '=')
757 goto parse_fail;
758
759 if (strcmp(attr_name, "linkid") == 0) {
760 if (read_int64(curr, &attr_buf) == 0)
761 goto parse_fail;
762 linkp->ll_linkid =
763 (datalink_class_t)*(int64_t *)attr_buf;
764 } else if (strcmp(attr_name, "name") == 0) {
765 if (read_str(curr, &attr_buf) == 0)
766 goto parse_fail;
767 (void) snprintf(linkp->ll_link,
768 MAXLINKNAMELEN, "%s", attr_buf);
769 } else if (strcmp(attr_name, "class") == 0) {
770 if (read_int64(curr, &attr_buf) == 0)
771 goto parse_fail;
772 linkp->ll_class =
773 (datalink_class_t)*(int64_t *)attr_buf;
774 } else if (strcmp(attr_name, "media") == 0) {
775 if (read_int64(curr, &attr_buf) == 0)
776 goto parse_fail;
777 linkp->ll_media =
778 (uint32_t)*(int64_t *)attr_buf;
779 } else if (strcmp(attr_name, "zone") == 0) {
780 if (read_str(curr, &attr_buf) == 0)
781 goto parse_fail;
782 linkp->ll_zoneid = getzoneidbyname(attr_buf);
783 if (linkp->ll_zoneid == -1) {
784 if (errno == EFAULT)
785 abort();
786 /*
787 * If we can't find the zone, assign the
788 * link to the GZ and mark it for being
789 * renamed.
790 */
791 linkp->ll_zoneid = 0;
792 rename = B_TRUE;
793 }
794 } else {
795 attr_buf_len = translators[type].read_func(curr,
796 &attr_buf);
797 if (attr_buf_len == 0)
798 goto parse_fail;
799
800 if (linkattr_set(&(linkp->ll_head), attr_name,
801 attr_buf, attr_buf_len, type) != 0) {
802 free(attr_buf);
803 goto parse_fail;
804 }
805 }
806
807 free(attr_buf);
808 attr_name[0] = '\0';
809 found_type = B_FALSE;
810 } else if (attr_name[0] != '\0') {
811 /*
812 * Non-zero length attr_name and found_type of false
813 * indicates that we have not found the type for this
814 * attribute. The pattern now is "<type>,<val>;", we
815 * want the <type> part of the pattern.
816 */
817 for (type = 0; type < ntranslators; type++) {
818 if (strcmp(curr,
819 translators[type].type_name) == 0) {
820 found_type = B_TRUE;
821 break;
822 }
823 }
824
825 if (!found_type)
826 goto parse_fail;
827 } else {
828 /*
829 * A zero length attr_name indicates we are looking
830 * at the beginning of a link attribute.
831 */
832 if (c != '=')
833 goto parse_fail;
834
835 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
836 }
837
838 /*
839 * The zone that this link belongs to has died, we are
840 * reparenting it to the GZ and renaming it to avoid name
841 * collisions.
842 */
843 if (rename == B_TRUE) {
844 (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
845 "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
846 }
847 curr = buf + i + 1;
848 }
849
850 /* Correct any erroneous IPTUN datalink class constant in the file */
851 if (linkp->ll_class == 0x60) {
852 linkp->ll_class = DATALINK_CLASS_IPTUN;
853 rewrite_needed = B_TRUE;
854 }
855
856 return (0);
857
858 parse_fail:
859 /*
860 * Free linkp->ll_head (link attribute list)
861 */
862 linkattr_destroy(linkp);
863 return (-1);
864 }
865
866 static boolean_t
867 process_link_line(char *buf, dlmgmt_link_t *linkp)
868 {
869 int i, len, llen;
870 char *str, *lasts;
871 char tmpbuf[MAXLINELEN];
872
873 bzero(linkp, sizeof (*linkp));
874 linkp->ll_linkid = DATALINK_INVALID_LINKID;
875
876 /*
877 * Use a copy of buf for parsing so that we can do whatever we want.
878 */
879 (void) strlcpy(tmpbuf, buf, MAXLINELEN);
880
881 /*
882 * Skip leading spaces, blank lines, and comments.
883 */
884 len = strlen(tmpbuf);
885 for (i = 0; i < len; i++) {
886 if (!isspace(tmpbuf[i]))
887 break;
888 }
889 if (i == len || tmpbuf[i] == '#')
890 return (B_TRUE);
891
892 str = tmpbuf + i;
893 /*
894 * Find the link name and assign it to the link structure.
895 */
896 if (strtok_r(str, " \n\t", &lasts) == NULL)
897 goto fail;
898
899 llen = strlen(str);
900 /*
901 * Note that a previous version of the persistent datalink.conf file
902 * stored the linkid as the first field. In that case, the name will
903 * be obtained through parse_linkprops from a property with the format
904 * "name=<linkname>". If we encounter such a format, we set
905 * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
906 * the new format after it's done reading in the data.
907 */
908 if (isdigit(str[0])) {
909 linkp->ll_linkid = atoi(str);
910 rewrite_needed = B_TRUE;
911 } else {
912 if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
913 sizeof (linkp->ll_link))
914 goto fail;
915 }
916
917 str += llen + 1;
918 if (str >= tmpbuf + len)
919 goto fail;
920
921 /*
922 * Now find the list of link properties.
923 */
924 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
925 goto fail;
926
927 if (parse_linkprops(str, linkp) < 0)
928 goto fail;
929
930 return (B_TRUE);
931
932 fail:
933 /*
934 * Delete corrupted line.
935 */
936 buf[0] = '\0';
937 return (B_FALSE);
938 }
939
940 /*
941 * Find any properties in linkp that refer to "old", and rename to "new".
942 * Return B_TRUE if any renaming occurred.
943 */
944 static int
945 dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
946 boolean_t *renamed)
947 {
948 dlmgmt_linkattr_t *attrp;
949 char *newval = NULL, *pname;
950 char valcp[MAXLINKATTRVALLEN];
951 size_t newsize;
952
953 *renamed = B_FALSE;
954
955 if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
956 (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
957 if (strcmp(old, (char *)attrp->lp_val) == 0) {
958 newsize = strlen(new) + 1;
959 if ((newval = malloc(newsize)) == NULL)
960 return (errno);
961 (void) strcpy(newval, new);
962 free(attrp->lp_val);
963 attrp->lp_val = newval;
964 attrp->lp_sz = newsize;
965 *renamed = B_TRUE;
966 }
967 return (0);
968 }
969
970 if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
971 return (0);
972
973 /* <linkname>:[<linkname>:]... */
974 if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
975 return (errno);
976
977 bcopy(attrp->lp_val, valcp, sizeof (valcp));
978 pname = strtok(valcp, ":");
979 while (pname != NULL) {
980 if (strcmp(pname, old) == 0) {
981 (void) strcat(newval, new);
982 *renamed = B_TRUE;
983 } else {
984 (void) strcat(newval, pname);
985 }
986 (void) strcat(newval, ":");
987 pname = strtok(NULL, ":");
988 }
989 if (*renamed) {
990 free(attrp->lp_val);
991 attrp->lp_val = newval;
992 attrp->lp_sz = strlen(newval) + 1;
993 } else {
994 free(newval);
995 }
996 return (0);
997 }
998
999 static int
1000 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
1001 {
1002 boolean_t done = B_FALSE;
1003 int err = 0;
1004 dlmgmt_link_t link_in_file, *linkp = NULL, *dblinkp;
1005 boolean_t persist = (req->ls_flags == DLMGMT_PERSIST);
1006 boolean_t writeall, rename, attr_renamed;
1007 char buf[MAXLINELEN];
1008
1009 writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
1010
1011 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
1012 /*
1013 * find the link in the avl tree with the given linkid.
1014 */
1015 linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
1016 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
1017 /*
1018 * This link has already been changed. This could
1019 * happen if the request is pending because of
1020 * read-only file-system. If so, we are done.
1021 */
1022 return (0);
1023 }
1024 /*
1025 * In the case of a rename, linkp's name has been updated to
1026 * the new name, and req->ls_link is the old link name.
1027 */
1028 rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
1029 }
1030
1031 /*
1032 * fp can be NULL if the file didn't initially exist and we're
1033 * creating it as part of this write operation.
1034 */
1035 if (fp == NULL)
1036 goto write;
1037
1038 while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
1039 process_link_line(buf, &link_in_file)) {
1040 /*
1041 * Only the link name is needed. Free the memory allocated for
1042 * the link attributes list of link_in_file.
1043 */
1044 linkattr_destroy(&link_in_file);
1045
1046 if (link_in_file.ll_link[0] == '\0' || done) {
1047 /*
1048 * this is a comment line or we are done updating the
1049 * line for the specified link, write the rest of
1050 * lines out.
1051 */
1052 if (fputs(buf, nfp) == EOF)
1053 err = errno;
1054 continue;
1055 }
1056
1057 switch (req->ls_op) {
1058 case DLMGMT_DB_OP_WRITE:
1059 /*
1060 * For write operations, we generate a new output line
1061 * if we're either writing all links (writeall) or if
1062 * the name of the link in the file matches the one
1063 * we're looking for. Otherwise, we write out the
1064 * buffer as-is.
1065 *
1066 * If we're doing a rename operation, ensure that any
1067 * references to the link being renamed in link
1068 * properties are also updated before we write
1069 * anything.
1070 */
1071 if (writeall) {
1072 linkp = link_by_name(link_in_file.ll_link,
1073 req->ls_zoneid);
1074 }
1075 if (writeall || strcmp(req->ls_link,
1076 link_in_file.ll_link) == 0) {
1077 generate_link_line(linkp, persist, buf);
1078 if (!writeall && !rename)
1079 done = B_TRUE;
1080 } else if (rename && persist) {
1081 dblinkp = link_by_name(link_in_file.ll_link,
1082 req->ls_zoneid);
1083 err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1084 linkp->ll_link, &attr_renamed);
1085 if (err != 0)
1086 break;
1087 if (attr_renamed) {
1088 generate_link_line(dblinkp, persist,
1089 buf);
1090 }
1091 }
1092 if (fputs(buf, nfp) == EOF)
1093 err = errno;
1094 break;
1095 case DLMGMT_DB_OP_DELETE:
1096 /*
1097 * Delete is simple. If buf does not represent the
1098 * link we're deleting, write it out.
1099 */
1100 if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1101 if (fputs(buf, nfp) == EOF)
1102 err = errno;
1103 } else {
1104 done = B_TRUE;
1105 }
1106 break;
1107 case DLMGMT_DB_OP_READ:
1108 default:
1109 err = EINVAL;
1110 break;
1111 }
1112 }
1113
1114 write:
1115 /*
1116 * If we get to the end of the file and have not seen what linkid
1117 * points to, write it out then.
1118 */
1119 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1120 generate_link_line(linkp, persist, buf);
1121 done = B_TRUE;
1122 if (fputs(buf, nfp) == EOF)
1123 err = errno;
1124 }
1125
1126 return (err);
1127 }
1128
1129 static int
1130 process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1131 {
1132 avl_index_t name_where, id_where;
1133 dlmgmt_link_t link_in_file, *newlink, *link_in_db;
1134 char buf[MAXLINELEN];
1135 int err = 0;
1136
1137 /*
1138 * This loop processes each line of the configuration file.
1139 */
1140 while (fgets(buf, MAXLINELEN, fp) != NULL) {
1141 if (!process_link_line(buf, &link_in_file)) {
1142 err = EINVAL;
1143 break;
1144 }
1145
1146 /*
1147 * Skip the comment line.
1148 */
1149 if (link_in_file.ll_link[0] == '\0') {
1150 linkattr_destroy(&link_in_file);
1151 continue;
1152 }
1153
1154 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1155 link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1156 linkattr_destroy(&link_in_file);
1157 continue;
1158 }
1159
1160 link_in_file.ll_zoneid = req->ls_zoneid;
1161 link_in_db = link_by_name(link_in_file.ll_link,
1162 link_in_file.ll_zoneid);
1163 if (link_in_db != NULL) {
1164 /*
1165 * If the link in the database already has the flag
1166 * for this request set, then the entry is a
1167 * duplicate. If it's not a duplicate, then simply
1168 * turn on the appropriate flag on the existing link.
1169 */
1170 if (link_in_db->ll_flags & req->ls_flags) {
1171 dlmgmt_log(LOG_WARNING, "Duplicate links "
1172 "in the repository: %s",
1173 link_in_file.ll_link);
1174 linkattr_destroy(&link_in_file);
1175 } else {
1176 if (req->ls_flags & DLMGMT_PERSIST) {
1177 /*
1178 * Save the newly read properties into
1179 * the existing link.
1180 */
1181 assert(link_in_db->ll_head == NULL);
1182 link_in_db->ll_head =
1183 link_in_file.ll_head;
1184 } else {
1185 linkattr_destroy(&link_in_file);
1186 }
1187 link_in_db->ll_flags |= req->ls_flags;
1188 }
1189 } else {
1190 /*
1191 * This is a new link. Allocate a new dlmgmt_link_t
1192 * and add it to the trees.
1193 */
1194 newlink = calloc(1, sizeof (*newlink));
1195 if (newlink == NULL) {
1196 dlmgmt_log(LOG_WARNING, "Unable to allocate "
1197 "memory to create new link %s",
1198 link_in_file.ll_link);
1199 linkattr_destroy(&link_in_file);
1200 continue;
1201 }
1202 bcopy(&link_in_file, newlink, sizeof (*newlink));
1203
1204 if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1205 newlink->ll_linkid = dlmgmt_nextlinkid;
1206 if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1207 NULL) {
1208 dlmgmt_log(LOG_WARNING, "Link ID %d is already"
1209 " in use, destroying link %s",
1210 newlink->ll_linkid, newlink->ll_link);
1211 link_destroy(newlink);
1212 continue;
1213 }
1214
1215 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1216 link_activate(newlink) != 0) {
1217 dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1218 newlink->ll_link);
1219 link_destroy(newlink);
1220 continue;
1221 }
1222
1223 avl_insert(&dlmgmt_id_avl, newlink, id_where);
1224 /*
1225 * link_activate call above can insert newlink in
1226 * dlmgmt_name_avl tree when activating a link that is
1227 * assigned to a NGZ.
1228 */
1229 if (avl_find(&dlmgmt_name_avl, newlink,
1230 &name_where) == NULL)
1231 avl_insert(&dlmgmt_name_avl, newlink,
1232 name_where);
1233
1234 dlmgmt_advance(newlink);
1235 newlink->ll_flags |= req->ls_flags;
1236 }
1237 }
1238
1239 return (err);
1240 }
1241
1242 /*
1243 * Generate an entry in the link database.
1244 * Each entry has this format:
1245 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1246 */
1247 static void
1248 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1249 {
1250 char tmpbuf[MAXLINELEN];
1251 char *ptr = tmpbuf;
1252 char *lim = tmpbuf + MAXLINELEN;
1253 dlmgmt_linkattr_t *cur_p = NULL;
1254 uint64_t u64;
1255
1256 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1257 if (!persist) {
1258 char zname[ZONENAME_MAX];
1259 /*
1260 * We store the linkid and the zone name in the active database
1261 * so that dlmgmtd can recover in the event that it is
1262 * restarted.
1263 */
1264 u64 = linkp->ll_linkid;
1265 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1266
1267 if (getzonenamebyid(linkp->ll_zoneid, zname,
1268 sizeof (zname)) != -1) {
1269 ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
1270 }
1271 }
1272 u64 = linkp->ll_class;
1273 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1274 u64 = linkp->ll_media;
1275 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1276
1277 /*
1278 * The daemon does not keep any active link attribute. Only store the
1279 * attributes if this request is for persistent configuration,
1280 */
1281 if (persist) {
1282 for (cur_p = linkp->ll_head; cur_p != NULL;
1283 cur_p = cur_p->lp_next) {
1284 ptr += translators[cur_p->lp_type].write_func(ptr,
1285 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1286 }
1287 }
1288
1289 if (ptr <= lim)
1290 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1291 }
1292
1293 int
1294 dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1295 {
1296 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1297 flags));
1298 }
1299
1300 int
1301 dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1302 uint32_t flags)
1303 {
1304 int err;
1305
1306 if (flags & DLMGMT_PERSIST) {
1307 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1308 linkp, DLMGMT_PERSIST)) != 0) {
1309 return (err);
1310 }
1311 }
1312
1313 if (flags & DLMGMT_ACTIVE) {
1314 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1315 linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1316 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1317 linkp, DLMGMT_PERSIST);
1318 return (err);
1319 }
1320 }
1321
1322 return (0);
1323 }
1324
1325 /*
1326 * Upgrade properties that have link IDs as values to link names. Because '.'
1327 * is a valid linkname character, the port separater for link aggregations
1328 * must be changed to ':'.
1329 */
1330 static void
1331 linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1332 {
1333 datalink_id_t linkid;
1334 char *portidstr;
1335 char portname[MAXLINKNAMELEN + 1];
1336 dlmgmt_link_t *linkp;
1337 char *new_attr_val;
1338 size_t new_attr_sz;
1339 boolean_t upgraded = B_FALSE;
1340
1341 if (strcmp(attrp->lp_name, "linkover") == 0 ||
1342 strcmp(attrp->lp_name, "simnetpeer") == 0) {
1343 if (attrp->lp_type == DLADM_TYPE_UINT64) {
1344 linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val;
1345 if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1346 return;
1347 new_attr_sz = strlen(linkp->ll_link) + 1;
1348 if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1349 return;
1350 (void) strcpy(new_attr_val, linkp->ll_link);
1351 upgraded = B_TRUE;
1352 }
1353 } else if (strcmp(attrp->lp_name, "portnames") == 0) {
1354 /*
1355 * The old format for "portnames" was
1356 * "<linkid>.[<linkid>.]...". The new format is
1357 * "<linkname>:[<linkname>:]...".
1358 */
1359 if (!isdigit(((char *)attrp->lp_val)[0]))
1360 return;
1361 new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1362 if (new_attr_val == NULL)
1363 return;
1364 portidstr = (char *)attrp->lp_val;
1365 while (*portidstr != '\0') {
1366 errno = 0;
1367 linkid = strtol(portidstr, &portidstr, 10);
1368 if (linkid == 0 || *portidstr != '.' ||
1369 (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1370 NULL) {
1371 free(new_attr_val);
1372 return;
1373 }
1374 (void) snprintf(portname, sizeof (portname), "%s:",
1375 linkp->ll_link);
1376 if (strlcat(new_attr_val, portname,
1377 MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1378 free(new_attr_val);
1379 return;
1380 }
1381 /* skip the '.' delimiter */
1382 portidstr++;
1383 }
1384 new_attr_sz = strlen(new_attr_val) + 1;
1385 upgraded = B_TRUE;
1386 }
1387
1388 if (upgraded) {
1389 attrp->lp_type = DLADM_TYPE_STR;
1390 attrp->lp_sz = new_attr_sz;
1391 free(attrp->lp_val);
1392 attrp->lp_val = new_attr_val;
1393 }
1394 }
1395
1396 static void
1397 dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1398 {
1399 dlmgmt_linkattr_t *attrp;
1400
1401 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1402 linkattr_upgrade(attrp);
1403 }
1404
1405 static void
1406 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1407 {
1408 linkp->ll_flags |= DLMGMT_ACTIVE;
1409 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1410 }
1411
1412 static void
1413 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1414 {
1415 dlmgmt_link_t *linkp;
1416
1417 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1418 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1419 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1420 func(linkp);
1421 }
1422 }
1423
1424 /*
1425 * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
1426 *
1427 * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
1428 * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
1429 * Because of the upcall architecture of dlmgmtd this can lead to deadlock
1430 * with the following scenario:
1431 * a) the thread preparing to fork will have acquired the malloc locks
1432 * then attempt to suspend every thread in preparation to fork.
1433 * b) all of the upcalls will be blocked in door_ucred() trying to malloc()
1434 * and get the credentials of their caller.
1435 * c) we can't suspend the in-kernel thread making the upcall.
1436 *
1437 * Thus, we cannot serve door requests because we're blocked in malloc()
1438 * which fork() owns, but fork() is in turn blocked on the in-kernel thread
1439 * making the door upcall. This is a fundamental architectural problem with
1440 * any server handling upcalls and also trying to fork().
1441 *
1442 * To minimize the chance of this deadlock occuring, we check ahead of time to
1443 * see if the file we want to read actually exists in the zone (which it almost
1444 * never does), so we don't need fork in that case (i.e. rarely to never).
1445 */
1446 static boolean_t
1447 zone_file_exists(char *zoneroot, char *filename)
1448 {
1449 struct stat sb;
1450 char fname[MAXPATHLEN];
1451
1452 (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
1453
1454 if (stat(fname, &sb) == -1)
1455 return (B_FALSE);
1456
1457 return (B_TRUE);
1458 }
1459
1460 /*
1461 * Initialize the datalink <link name, linkid> mapping and the link's
1462 * attributes list based on the configuration file /etc/dladm/datalink.conf
1463 * and the active configuration cache file
1464 * /etc/svc/volatile/dladm/datalink-management:default.cache.
1465 */
1466 int
1467 dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
1468 {
1469 dlmgmt_db_req_t *req;
1470 int err;
1471 boolean_t boot = B_FALSE;
1472 char tdir[MAXPATHLEN];
1473 char *path = cachefile;
1474
1475 if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1476 DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1477 return (err);
1478
1479 /* Handle running in a non-native branded zone (i.e. has /native) */
1480 if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) {
1481 (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
1482 path = tdir;
1483 }
1484
1485 if (zone_file_exists(zoneroot, path)) {
1486 if ((err = dlmgmt_process_db_req(req)) != 0) {
1487 /*
1488 * If we get back ENOENT, that means that the active
1489 * configuration file doesn't exist yet, and is not an
1490 * error. We'll create it down below after we've
1491 * loaded the persistent configuration.
1492 */
1493 if (err != ENOENT)
1494 goto done;
1495 boot = B_TRUE;
1496 }
1497 } else {
1498 boot = B_TRUE;
1499 }
1500
1501 if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) {
1502 req->ls_flags = DLMGMT_PERSIST;
1503 err = dlmgmt_process_db_req(req);
1504 if (err != 0 && err != ENOENT)
1505 goto done;
1506 }
1507 err = 0;
1508 if (rewrite_needed) {
1509 /*
1510 * First update links in memory, then dump the entire db to
1511 * disk.
1512 */
1513 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1514 req->ls_op = DLMGMT_DB_OP_WRITE;
1515 req->ls_linkid = DATALINK_ALL_LINKID;
1516 if ((err = dlmgmt_process_db_req(req)) != 0 &&
1517 err != EINPROGRESS)
1518 goto done;
1519 }
1520 if (boot) {
1521 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1522 dlmgmt_db_phys_activate);
1523 }
1524
1525 done:
1526 if (err == EINPROGRESS)
1527 err = 0;
1528 else
1529 free(req);
1530 return (err);
1531 }
1532
1533 /*
1534 * Remove all links in the given zoneid.
1535 */
1536 void
1537 dlmgmt_db_fini(zoneid_t zoneid)
1538 {
1539 dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1540
1541 while (linkp != NULL) {
1542 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1543 if (linkp->ll_zoneid == zoneid) {
1544 (void) dlmgmt_destroy_common(linkp,
1545 DLMGMT_ACTIVE | DLMGMT_PERSIST);
1546 }
1547 linkp = next_linkp;
1548 }
1549 }