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 */
25
26 #include <assert.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <syslog.h>
35 #include <zone.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <stropts.h>
39 #include <sys/conf.h>
40 #include <pthread.h>
41 #include <unistd.h>
42 #include <wait.h>
43 #include <libcontract.h>
44 #include <libcontract_priv.h>
45 #include <sys/contract/process.h>
46 #include "dlmgmt_impl.h"
47
48 typedef enum dlmgmt_db_op {
49 DLMGMT_DB_OP_WRITE,
50 DLMGMT_DB_OP_DELETE,
51 DLMGMT_DB_OP_READ
52 } dlmgmt_db_op_t;
53
54 typedef struct dlmgmt_db_req_s {
55 struct dlmgmt_db_req_s *ls_next;
56 dlmgmt_db_op_t ls_op;
57 char ls_link[MAXLINKNAMELEN];
58 datalink_id_t ls_linkid;
59 zoneid_t ls_zoneid;
60 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */
61 /* DLMGMT_PERSIST, not both. */
62 } dlmgmt_db_req_t;
63
64 /*
65 * List of pending db updates (e.g., because of a read-only filesystem).
535 return (req);
536 }
537
538 /*
539 * Update the db entry with name "entryname" using information from "linkp".
540 */
541 static int
542 dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
543 uint32_t flags)
544 {
545 dlmgmt_db_req_t *req;
546 int err;
547
548 /* It is either a persistent request or an active request, not both. */
549 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
550
551 if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
552 linkp->ll_zoneid, flags, &err)) == NULL)
553 return (err);
554
555 /*
556 * If the return error is EINPROGRESS, this request is handled
557 * asynchronously; return success.
558 */
559 err = dlmgmt_process_db_req(req);
560 if (err != EINPROGRESS)
561 free(req);
562 else
563 err = 0;
564 return (err);
565 }
566
567 #define DLMGMT_DB_OP_STR(op) \
568 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
569 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
570
571 #define DLMGMT_DB_CONF_STR(flag) \
572 (((flag) == DLMGMT_ACTIVE) ? "active" : \
573 (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
574
697 assert(dlmgmt_db_req_head == NULL);
698 dlmgmt_db_req_tail = NULL;
699 }
700 free(req);
701 }
702
703 dlmgmt_table_unlock();
704 return (NULL);
705 }
706
707 static int
708 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
709 {
710 boolean_t found_type = B_FALSE;
711 dladm_datatype_t type = DLADM_TYPE_STR;
712 int i, len;
713 char *curr;
714 char attr_name[MAXLINKATTRLEN];
715 size_t attr_buf_len = 0;
716 void *attr_buf = NULL;
717
718 curr = buf;
719 len = strlen(buf);
720 attr_name[0] = '\0';
721 for (i = 0; i < len; i++) {
722 char c = buf[i];
723 boolean_t match = (c == '=' ||
724 (c == ',' && !found_type) || c == ';');
725
726 /*
727 * Move to the next character if there is no match and
728 * if we have not reached the last character.
729 */
730 if (!match && i != len - 1)
731 continue;
732
733 if (match) {
734 /*
735 * NUL-terminate the string pointed to by 'curr'.
736 */
737 buf[i] = '\0';
738 if (*curr == '\0')
739 goto parse_fail;
740 }
741
742 if (attr_name[0] != '\0' && found_type) {
743 /*
744 * We get here after we have processed the "<prop>="
745 * pattern. The pattern we are now interested in is
751 if (strcmp(attr_name, "linkid") == 0) {
752 if (read_int64(curr, &attr_buf) == 0)
753 goto parse_fail;
754 linkp->ll_linkid =
755 (datalink_class_t)*(int64_t *)attr_buf;
756 } else if (strcmp(attr_name, "name") == 0) {
757 if (read_str(curr, &attr_buf) == 0)
758 goto parse_fail;
759 (void) snprintf(linkp->ll_link,
760 MAXLINKNAMELEN, "%s", attr_buf);
761 } else if (strcmp(attr_name, "class") == 0) {
762 if (read_int64(curr, &attr_buf) == 0)
763 goto parse_fail;
764 linkp->ll_class =
765 (datalink_class_t)*(int64_t *)attr_buf;
766 } else if (strcmp(attr_name, "media") == 0) {
767 if (read_int64(curr, &attr_buf) == 0)
768 goto parse_fail;
769 linkp->ll_media =
770 (uint32_t)*(int64_t *)attr_buf;
771 } else {
772 attr_buf_len = translators[type].read_func(curr,
773 &attr_buf);
774 if (attr_buf_len == 0)
775 goto parse_fail;
776
777 if (linkattr_set(&(linkp->ll_head), attr_name,
778 attr_buf, attr_buf_len, type) != 0) {
779 free(attr_buf);
780 goto parse_fail;
781 }
782 }
783
784 free(attr_buf);
785 attr_name[0] = '\0';
786 found_type = B_FALSE;
787 } else if (attr_name[0] != '\0') {
788 /*
789 * Non-zero length attr_name and found_type of false
790 * indicates that we have not found the type for this
794 for (type = 0; type < ntranslators; type++) {
795 if (strcmp(curr,
796 translators[type].type_name) == 0) {
797 found_type = B_TRUE;
798 break;
799 }
800 }
801
802 if (!found_type)
803 goto parse_fail;
804 } else {
805 /*
806 * A zero length attr_name indicates we are looking
807 * at the beginning of a link attribute.
808 */
809 if (c != '=')
810 goto parse_fail;
811
812 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
813 }
814 curr = buf + i + 1;
815 }
816
817 /* Correct any erroneous IPTUN datalink class constant in the file */
818 if (linkp->ll_class == 0x60) {
819 linkp->ll_class = DATALINK_CLASS_IPTUN;
820 rewrite_needed = B_TRUE;
821 }
822
823 return (0);
824
825 parse_fail:
826 /*
827 * Free linkp->ll_head (link attribute list)
828 */
829 linkattr_destroy(linkp);
830 return (-1);
831 }
832
833 static boolean_t
1205
1206 return (err);
1207 }
1208
1209 /*
1210 * Generate an entry in the link database.
1211 * Each entry has this format:
1212 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1213 */
1214 static void
1215 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1216 {
1217 char tmpbuf[MAXLINELEN];
1218 char *ptr = tmpbuf;
1219 char *lim = tmpbuf + MAXLINELEN;
1220 dlmgmt_linkattr_t *cur_p = NULL;
1221 uint64_t u64;
1222
1223 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1224 if (!persist) {
1225 /*
1226 * We store the linkid in the active database so that dlmgmtd
1227 * can recover in the event that it is restarted.
1228 */
1229 u64 = linkp->ll_linkid;
1230 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1231 }
1232 u64 = linkp->ll_class;
1233 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1234 u64 = linkp->ll_media;
1235 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1236
1237 /*
1238 * The daemon does not keep any active link attribute. Only store the
1239 * attributes if this request is for persistent configuration,
1240 */
1241 if (persist) {
1242 for (cur_p = linkp->ll_head; cur_p != NULL;
1243 cur_p = cur_p->lp_next) {
1244 ptr += translators[cur_p->lp_type].write_func(ptr,
1245 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1246 }
1247 }
1248
1249 if (ptr <= lim)
1250 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1251 }
1365 static void
1366 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1367 {
1368 linkp->ll_flags |= DLMGMT_ACTIVE;
1369 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1370 }
1371
1372 static void
1373 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1374 {
1375 dlmgmt_link_t *linkp;
1376
1377 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1378 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1379 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1380 func(linkp);
1381 }
1382 }
1383
1384 /*
1385 * Initialize the datalink <link name, linkid> mapping and the link's
1386 * attributes list based on the configuration file /etc/dladm/datalink.conf
1387 * and the active configuration cache file
1388 * /etc/svc/volatile/dladm/datalink-management:default.cache.
1389 */
1390 int
1391 dlmgmt_db_init(zoneid_t zoneid)
1392 {
1393 dlmgmt_db_req_t *req;
1394 int err;
1395 boolean_t boot = B_FALSE;
1396
1397 if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1398 DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1399 return (err);
1400
1401 if ((err = dlmgmt_process_db_req(req)) != 0) {
1402 /*
1403 * If we get back ENOENT, that means that the active
1404 * configuration file doesn't exist yet, and is not an error.
1405 * We'll create it down below after we've loaded the
1406 * persistent configuration.
1407 */
1408 if (err != ENOENT)
1409 goto done;
1410 boot = B_TRUE;
1411 }
1412
1413 req->ls_flags = DLMGMT_PERSIST;
1414 err = dlmgmt_process_db_req(req);
1415 if (err != 0 && err != ENOENT)
1416 goto done;
1417 err = 0;
1418 if (rewrite_needed) {
1419 /*
1420 * First update links in memory, then dump the entire db to
1421 * disk.
1422 */
1423 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1424 req->ls_op = DLMGMT_DB_OP_WRITE;
1425 req->ls_linkid = DATALINK_ALL_LINKID;
1426 if ((err = dlmgmt_process_db_req(req)) != 0 &&
1427 err != EINPROGRESS)
1428 goto done;
1429 }
1430 if (boot) {
1431 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1432 dlmgmt_db_phys_activate);
1433 }
1434
1435 done:
1436 if (err == EINPROGRESS)
1437 err = 0;
1438 else
1439 free(req);
1440 return (err);
1441 }
1442
1443 /*
1444 * Remove all links in the given zoneid.
1445 */
1446 void
1447 dlmgmt_db_fini(zoneid_t zoneid)
1448 {
1449 dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1450
1451 while (linkp != NULL) {
1452 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1453 if (linkp->ll_zoneid == zoneid) {
1454 (void) dlmgmt_destroy_common(linkp,
1455 DLMGMT_ACTIVE | DLMGMT_PERSIST);
1456 }
1457 linkp = next_linkp;
1458 }
1459 }
|
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 2014, Joyent Inc. All rights reserved.
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 <sys/vnic.h>
48 #include <zone.h>
49 #include "dlmgmt_impl.h"
50
51 typedef enum dlmgmt_db_op {
52 DLMGMT_DB_OP_WRITE,
53 DLMGMT_DB_OP_DELETE,
54 DLMGMT_DB_OP_READ
55 } dlmgmt_db_op_t;
56
57 typedef struct dlmgmt_db_req_s {
58 struct dlmgmt_db_req_s *ls_next;
59 dlmgmt_db_op_t ls_op;
60 char ls_link[MAXLINKNAMELEN];
61 datalink_id_t ls_linkid;
62 zoneid_t ls_zoneid;
63 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */
64 /* DLMGMT_PERSIST, not both. */
65 } dlmgmt_db_req_t;
66
67 /*
68 * List of pending db updates (e.g., because of a read-only filesystem).
538 return (req);
539 }
540
541 /*
542 * Update the db entry with name "entryname" using information from "linkp".
543 */
544 static int
545 dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
546 uint32_t flags)
547 {
548 dlmgmt_db_req_t *req;
549 int err;
550
551 /* It is either a persistent request or an active request, not both. */
552 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
553
554 if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
555 linkp->ll_zoneid, flags, &err)) == NULL)
556 return (err);
557
558 /* If transient op and onloan, use the global zone cache file. */
559 if (flags == DLMGMT_ACTIVE && linkp->ll_onloan)
560 req->ls_zoneid = GLOBAL_ZONEID;
561
562 /*
563 * If the return error is EINPROGRESS, this request is handled
564 * asynchronously; return success.
565 */
566 err = dlmgmt_process_db_req(req);
567 if (err != EINPROGRESS)
568 free(req);
569 else
570 err = 0;
571 return (err);
572 }
573
574 #define DLMGMT_DB_OP_STR(op) \
575 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
576 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
577
578 #define DLMGMT_DB_CONF_STR(flag) \
579 (((flag) == DLMGMT_ACTIVE) ? "active" : \
580 (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
581
704 assert(dlmgmt_db_req_head == NULL);
705 dlmgmt_db_req_tail = NULL;
706 }
707 free(req);
708 }
709
710 dlmgmt_table_unlock();
711 return (NULL);
712 }
713
714 static int
715 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
716 {
717 boolean_t found_type = B_FALSE;
718 dladm_datatype_t type = DLADM_TYPE_STR;
719 int i, len;
720 char *curr;
721 char attr_name[MAXLINKATTRLEN];
722 size_t attr_buf_len = 0;
723 void *attr_buf = NULL;
724 boolean_t rename;
725
726 curr = buf;
727 len = strlen(buf);
728 attr_name[0] = '\0';
729 for (i = 0; i < len; i++) {
730 char c = buf[i];
731 boolean_t match = (c == '=' ||
732 (c == ',' && !found_type) || c == ';');
733
734 rename = B_FALSE;
735
736 /*
737 * Move to the next character if there is no match and
738 * if we have not reached the last character.
739 */
740 if (!match && i != len - 1)
741 continue;
742
743 if (match) {
744 /*
745 * NUL-terminate the string pointed to by 'curr'.
746 */
747 buf[i] = '\0';
748 if (*curr == '\0')
749 goto parse_fail;
750 }
751
752 if (attr_name[0] != '\0' && found_type) {
753 /*
754 * We get here after we have processed the "<prop>="
755 * pattern. The pattern we are now interested in is
761 if (strcmp(attr_name, "linkid") == 0) {
762 if (read_int64(curr, &attr_buf) == 0)
763 goto parse_fail;
764 linkp->ll_linkid =
765 (datalink_class_t)*(int64_t *)attr_buf;
766 } else if (strcmp(attr_name, "name") == 0) {
767 if (read_str(curr, &attr_buf) == 0)
768 goto parse_fail;
769 (void) snprintf(linkp->ll_link,
770 MAXLINKNAMELEN, "%s", attr_buf);
771 } else if (strcmp(attr_name, "class") == 0) {
772 if (read_int64(curr, &attr_buf) == 0)
773 goto parse_fail;
774 linkp->ll_class =
775 (datalink_class_t)*(int64_t *)attr_buf;
776 } else if (strcmp(attr_name, "media") == 0) {
777 if (read_int64(curr, &attr_buf) == 0)
778 goto parse_fail;
779 linkp->ll_media =
780 (uint32_t)*(int64_t *)attr_buf;
781 } else if (strcmp(attr_name, "zone") == 0) {
782 if (read_str(curr, &attr_buf) == 0)
783 goto parse_fail;
784 linkp->ll_zoneid = getzoneidbyname(attr_buf);
785 if (linkp->ll_zoneid == -1) {
786 if (errno == EFAULT)
787 abort();
788 /*
789 * If we can't find the zone, assign the
790 * link to the GZ and mark it for being
791 * renamed.
792 */
793 linkp->ll_zoneid = 0;
794 rename = B_TRUE;
795 }
796 } else {
797 attr_buf_len = translators[type].read_func(curr,
798 &attr_buf);
799 if (attr_buf_len == 0)
800 goto parse_fail;
801
802 if (linkattr_set(&(linkp->ll_head), attr_name,
803 attr_buf, attr_buf_len, type) != 0) {
804 free(attr_buf);
805 goto parse_fail;
806 }
807 }
808
809 free(attr_buf);
810 attr_name[0] = '\0';
811 found_type = B_FALSE;
812 } else if (attr_name[0] != '\0') {
813 /*
814 * Non-zero length attr_name and found_type of false
815 * indicates that we have not found the type for this
819 for (type = 0; type < ntranslators; type++) {
820 if (strcmp(curr,
821 translators[type].type_name) == 0) {
822 found_type = B_TRUE;
823 break;
824 }
825 }
826
827 if (!found_type)
828 goto parse_fail;
829 } else {
830 /*
831 * A zero length attr_name indicates we are looking
832 * at the beginning of a link attribute.
833 */
834 if (c != '=')
835 goto parse_fail;
836
837 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
838 }
839
840 /*
841 * The zone that this link belongs to has died, we are
842 * reparenting it to the GZ and renaming it to avoid name
843 * collisions.
844 */
845 if (rename == B_TRUE) {
846 (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
847 "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
848 }
849 curr = buf + i + 1;
850 }
851
852 /* Correct any erroneous IPTUN datalink class constant in the file */
853 if (linkp->ll_class == 0x60) {
854 linkp->ll_class = DATALINK_CLASS_IPTUN;
855 rewrite_needed = B_TRUE;
856 }
857
858 return (0);
859
860 parse_fail:
861 /*
862 * Free linkp->ll_head (link attribute list)
863 */
864 linkattr_destroy(linkp);
865 return (-1);
866 }
867
868 static boolean_t
1240
1241 return (err);
1242 }
1243
1244 /*
1245 * Generate an entry in the link database.
1246 * Each entry has this format:
1247 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1248 */
1249 static void
1250 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1251 {
1252 char tmpbuf[MAXLINELEN];
1253 char *ptr = tmpbuf;
1254 char *lim = tmpbuf + MAXLINELEN;
1255 dlmgmt_linkattr_t *cur_p = NULL;
1256 uint64_t u64;
1257
1258 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1259 if (!persist) {
1260 char zname[ZONENAME_MAX];
1261 /*
1262 * We store the linkid and the zone name in the active database
1263 * so that dlmgmtd can recover in the event that it is
1264 * restarted.
1265 */
1266 u64 = linkp->ll_linkid;
1267 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1268
1269 if (getzonenamebyid(linkp->ll_zoneid, zname,
1270 sizeof (zname)) != -1) {
1271 ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
1272 }
1273 }
1274 u64 = linkp->ll_class;
1275 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1276 u64 = linkp->ll_media;
1277 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1278
1279 /*
1280 * The daemon does not keep any active link attribute. Only store the
1281 * attributes if this request is for persistent configuration,
1282 */
1283 if (persist) {
1284 for (cur_p = linkp->ll_head; cur_p != NULL;
1285 cur_p = cur_p->lp_next) {
1286 ptr += translators[cur_p->lp_type].write_func(ptr,
1287 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1288 }
1289 }
1290
1291 if (ptr <= lim)
1292 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1293 }
1407 static void
1408 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1409 {
1410 linkp->ll_flags |= DLMGMT_ACTIVE;
1411 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1412 }
1413
1414 static void
1415 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1416 {
1417 dlmgmt_link_t *linkp;
1418
1419 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1420 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1421 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1422 func(linkp);
1423 }
1424 }
1425
1426 /*
1427 * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
1428 *
1429 * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
1430 * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
1431 * Because of the upcall architecture of dlmgmtd this can lead to deadlock
1432 * with the following scenario:
1433 * a) the thread preparing to fork will have acquired the malloc locks
1434 * then attempt to suspend every thread in preparation to fork.
1435 * b) all of the upcalls will be blocked in door_ucred() trying to malloc()
1436 * and get the credentials of their caller.
1437 * c) we can't suspend the in-kernel thread making the upcall.
1438 *
1439 * Thus, we cannot serve door requests because we're blocked in malloc()
1440 * which fork() owns, but fork() is in turn blocked on the in-kernel thread
1441 * making the door upcall. This is a fundamental architectural problem with
1442 * any server handling upcalls and also trying to fork().
1443 *
1444 * To minimize the chance of this deadlock occuring, we check ahead of time to
1445 * see if the file we want to read actually exists in the zone (which it almost
1446 * never does), so we don't need fork in that case (i.e. rarely to never).
1447 */
1448 static boolean_t
1449 zone_file_exists(char *zoneroot, char *filename)
1450 {
1451 struct stat sb;
1452 char fname[MAXPATHLEN];
1453
1454 (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
1455
1456 if (stat(fname, &sb) == -1)
1457 return (B_FALSE);
1458
1459 return (B_TRUE);
1460 }
1461
1462 /*
1463 * Initialize the datalink <link name, linkid> mapping and the link's
1464 * attributes list based on the configuration file /etc/dladm/datalink.conf
1465 * and the active configuration cache file
1466 * /etc/svc/volatile/dladm/datalink-management:default.cache.
1467 */
1468 int
1469 dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
1470 {
1471 dlmgmt_db_req_t *req;
1472 int err;
1473 boolean_t boot = B_FALSE;
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 char tdir[MAXPATHLEN];
1482
1483 (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
1484 (void) strlcpy(cachefile, tdir, sizeof (cachefile));
1485 }
1486
1487 if (zone_file_exists(zoneroot, cachefile)) {
1488 if ((err = dlmgmt_process_db_req(req)) != 0) {
1489 /*
1490 * If we get back ENOENT, that means that the active
1491 * configuration file doesn't exist yet, and is not an
1492 * error. We'll create it down below after we've
1493 * loaded the persistent configuration.
1494 */
1495 if (err != ENOENT)
1496 goto done;
1497 boot = B_TRUE;
1498 }
1499 } else {
1500 boot = B_TRUE;
1501 }
1502
1503 if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) {
1504 req->ls_flags = DLMGMT_PERSIST;
1505 err = dlmgmt_process_db_req(req);
1506 if (err != 0 && err != ENOENT)
1507 goto done;
1508 }
1509 err = 0;
1510 if (rewrite_needed) {
1511 /*
1512 * First update links in memory, then dump the entire db to
1513 * disk.
1514 */
1515 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1516 req->ls_op = DLMGMT_DB_OP_WRITE;
1517 req->ls_linkid = DATALINK_ALL_LINKID;
1518 if ((err = dlmgmt_process_db_req(req)) != 0 &&
1519 err != EINPROGRESS)
1520 goto done;
1521 }
1522 if (boot) {
1523 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1524 dlmgmt_db_phys_activate);
1525 }
1526
1527 done:
1528 if (err == EINPROGRESS)
1529 err = 0;
1530 else
1531 free(req);
1532 return (err);
1533 }
1534
1535 /*
1536 * Remove all links in the given zoneid.
1537 *
1538 * We do this work in two different passes. In the first pass, we remove any
1539 * entry that hasn't been loaned and mark every entry that has been loaned as
1540 * something that is going to be tombstomed. In the second pass, we drop the
1541 * table lock for every entry and remove the tombstombed entry for our zone.
1542 */
1543 void
1544 dlmgmt_db_fini(zoneid_t zoneid)
1545 {
1546 dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1547
1548 while (linkp != NULL) {
1549 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1550 if (linkp->ll_zoneid == zoneid) {
1551 boolean_t onloan = linkp->ll_onloan;
1552
1553 /*
1554 * Cleanup any VNICs that were loaned to the zone
1555 * before the zone goes away and we can no longer
1556 * refer to the VNIC by the name/zoneid.
1557 */
1558 if (onloan) {
1559 (void) dlmgmt_delete_db_entry(linkp,
1560 DLMGMT_ACTIVE);
1561 linkp->ll_tomb = B_TRUE;
1562 } else {
1563 (void) dlmgmt_destroy_common(linkp,
1564 DLMGMT_ACTIVE | DLMGMT_PERSIST);
1565 }
1566
1567 }
1568 linkp = next_linkp;
1569 }
1570
1571 again:
1572 linkp = avl_first(&dlmgmt_name_avl);
1573 while (linkp != NULL) {
1574 vnic_ioc_delete_t ioc;
1575
1576 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1577
1578 if (linkp->ll_zoneid != zoneid) {
1579 linkp = next_linkp;
1580 continue;
1581 }
1582 ioc.vd_vnic_id = linkp->ll_linkid;
1583 if (linkp->ll_tomb != B_TRUE)
1584 abort();
1585
1586 /*
1587 * We have to drop the table lock while going up into the
1588 * kernel. If we hold the table lock while deleting a vnic, we
1589 * may get blocked on the mac perimeter and the holder of it may
1590 * want something from dlmgmtd.
1591 */
1592 dlmgmt_table_unlock();
1593
1594 if (ioctl(dladm_dld_fd(dld_handle),
1595 VNIC_IOC_DELETE, &ioc) < 0)
1596 dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini "
1597 "delete VNIC ioctl failed %d %d",
1598 ioc.vd_vnic_id, errno);
1599
1600 /*
1601 * Even though we've dropped the lock, we know that nothing else
1602 * could have removed us. Therefore, it should be safe to go
1603 * through and delete ourselves, but do nothing else. We'll have
1604 * to restart iteration from the beginning. This can be painful.
1605 */
1606 dlmgmt_table_lock(B_TRUE);
1607
1608 (void) dlmgmt_destroy_common(linkp,
1609 DLMGMT_ACTIVE | DLMGMT_PERSIST);
1610 goto again;
1611 }
1612
1613 }
|