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>
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
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>="
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
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
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 */
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,
|
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 * Copyright 2023 Oxide Computer Company
26 */
27
28 #include <assert.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <syslog.h>
37 #include <zone.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <stropts.h>
41 #include <sys/conf.h>
42 #include <pthread.h>
43 #include <unistd.h>
44 #include <wait.h>
45 #include <libcontract.h>
535 req->ls_zoneid = zoneid;
536 req->ls_flags = flags;
537 }
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
559 /*
560 * If this is a transient link, then use the global zone cache file.
561 * This is in order to allow recovery from a dlmgmtd failure that
562 * leaves a zone in a 'down' state. In that state it is not possible
563 * to read the zone's cache file (since it is always done from a sub
564 * process running in the zone's context). As a result, datalinks would
565 * otherwise remain stuck in the zone.
566 */
567 if (flags == DLMGMT_ACTIVE && linkp->ll_transient)
568 req->ls_zoneid = GLOBAL_ZONEID;
569
570 /* If transient op and onloan, use the global zone cache file. */
571 if (flags == DLMGMT_ACTIVE && linkp->ll_onloan)
572 req->ls_zoneid = GLOBAL_ZONEID;
573
574 /*
575 * If the return error is EINPROGRESS, this request is handled
576 * asynchronously; return success.
577 */
578 err = dlmgmt_process_db_req(req);
579 if (err != EINPROGRESS)
580 free(req);
581 else
582 err = 0;
583 return (err);
584 }
585
586 #define DLMGMT_DB_OP_STR(op) \
587 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
588 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
589
716 assert(dlmgmt_db_req_head == NULL);
717 dlmgmt_db_req_tail = NULL;
718 }
719 free(req);
720 }
721
722 dlmgmt_table_unlock();
723 return (NULL);
724 }
725
726 static int
727 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
728 {
729 boolean_t found_type = B_FALSE;
730 dladm_datatype_t type = DLADM_TYPE_STR;
731 int i, len;
732 char *curr;
733 char attr_name[MAXLINKATTRLEN];
734 size_t attr_buf_len = 0;
735 void *attr_buf = NULL;
736
737 curr = buf;
738 len = strlen(buf);
739 attr_name[0] = '\0';
740 for (i = 0; i < len; i++) {
741 char c = buf[i];
742 boolean_t match = (c == '=' ||
743 (c == ',' && !found_type) || c == ';');
744 boolean_t rename = B_FALSE;
745
746 /*
747 * Move to the next character if there is no match and
748 * if we have not reached the last character.
749 */
750 if (!match && i != len - 1)
751 continue;
752
753 if (match) {
754 /*
755 * NUL-terminate the string pointed to by 'curr'.
756 */
757 buf[i] = '\0';
758 if (*curr == '\0')
759 goto parse_fail;
760 }
761
762 if (attr_name[0] != '\0' && found_type) {
763 /*
764 * We get here after we have processed the "<prop>="
786 } else if (strcmp(attr_name, "media") == 0) {
787 if (read_int64(curr, &attr_buf) == 0)
788 goto parse_fail;
789 linkp->ll_media =
790 (uint32_t)*(int64_t *)attr_buf;
791 } else if (strcmp(attr_name, "zone") == 0) {
792 if (read_str(curr, &attr_buf) == 0)
793 goto parse_fail;
794 linkp->ll_zoneid = getzoneidbyname(attr_buf);
795 if (linkp->ll_zoneid == -1) {
796 if (errno == EFAULT)
797 abort();
798 /*
799 * If we can't find the zone, assign the
800 * link to the GZ and mark it for being
801 * renamed.
802 */
803 linkp->ll_zoneid = 0;
804 rename = B_TRUE;
805 }
806 } else if (strcmp(attr_name, "transient") == 0) {
807 if (read_boolean(curr, &attr_buf) == 0)
808 goto parse_fail;
809 linkp->ll_transient = *(boolean_t *)attr_buf;
810 } else {
811 attr_buf_len = translators[type].read_func(curr,
812 &attr_buf);
813 if (attr_buf_len == 0)
814 goto parse_fail;
815
816 if (linkattr_set(&(linkp->ll_head), attr_name,
817 attr_buf, attr_buf_len, type) != 0) {
818 free(attr_buf);
819 goto parse_fail;
820 }
821 }
822
823 free(attr_buf);
824 attr_name[0] = '\0';
825 found_type = B_FALSE;
826 } else if (attr_name[0] != '\0') {
827 /*
828 * Non-zero length attr_name and found_type of false
829 * indicates that we have not found the type for this
839 }
840
841 if (!found_type)
842 goto parse_fail;
843 } else {
844 /*
845 * A zero length attr_name indicates we are looking
846 * at the beginning of a link attribute.
847 */
848 if (c != '=')
849 goto parse_fail;
850
851 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
852 }
853
854 /*
855 * The zone that this link belongs to has died, we are
856 * reparenting it to the GZ and renaming it to avoid name
857 * collisions.
858 */
859 if (rename) {
860 (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
861 "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
862 }
863
864 curr = buf + i + 1;
865 }
866
867 /* Correct any erroneous IPTUN datalink class constant in the file */
868 if (linkp->ll_class == 0x60) {
869 linkp->ll_class = DATALINK_CLASS_IPTUN;
870 rewrite_needed = B_TRUE;
871 }
872
873 return (0);
874
875 parse_fail:
876 /*
877 * Free linkp->ll_head (link attribute list)
878 */
879 linkattr_destroy(linkp);
880 return (-1);
881 }
882
883 static boolean_t
1157 while (fgets(buf, MAXLINELEN, fp) != NULL) {
1158 if (!process_link_line(buf, &link_in_file)) {
1159 err = EINVAL;
1160 break;
1161 }
1162
1163 /*
1164 * Skip the comment line.
1165 */
1166 if (link_in_file.ll_link[0] == '\0') {
1167 linkattr_destroy(&link_in_file);
1168 continue;
1169 }
1170
1171 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1172 link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1173 linkattr_destroy(&link_in_file);
1174 continue;
1175 }
1176
1177 assert(req->ls_zoneid == 0 ||
1178 link_in_file.ll_zoneid == req->ls_zoneid);
1179 link_in_db = link_by_name(link_in_file.ll_link,
1180 link_in_file.ll_zoneid);
1181 if (link_in_db != NULL) {
1182 /*
1183 * If the link in the database already has the flag
1184 * for this request set, then the entry is a
1185 * duplicate. If it's not a duplicate, then simply
1186 * turn on the appropriate flag on the existing link.
1187 */
1188 if (link_in_db->ll_flags & req->ls_flags) {
1189 dlmgmt_log(LOG_WARNING, "Duplicate links "
1190 "in the repository: %s",
1191 link_in_file.ll_link);
1192 linkattr_destroy(&link_in_file);
1193 } else {
1194 if (req->ls_flags & DLMGMT_PERSIST) {
1195 /*
1196 * Save the newly read properties into
1197 * the existing link.
1198 */
1257 return (err);
1258 }
1259
1260 /*
1261 * Generate an entry in the link database.
1262 * Each entry has this format:
1263 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1264 */
1265 static void
1266 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1267 {
1268 char tmpbuf[MAXLINELEN];
1269 char *ptr = tmpbuf;
1270 char *lim = tmpbuf + MAXLINELEN;
1271 dlmgmt_linkattr_t *cur_p = NULL;
1272 uint64_t u64;
1273
1274 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1275 if (!persist) {
1276 char zname[ZONENAME_MAX];
1277
1278 /*
1279 * We store the linkid and the zone name in the active database
1280 * so that dlmgmtd can recover in the event that it is
1281 * restarted.
1282 */
1283 u64 = linkp->ll_linkid;
1284 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1285
1286 if (getzonenamebyid(linkp->ll_zoneid, zname,
1287 sizeof (zname)) != -1) {
1288 ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
1289 }
1290 }
1291 u64 = linkp->ll_class;
1292 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1293 u64 = linkp->ll_media;
1294 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1295
1296 if (!persist && linkp->ll_transient) {
1297 boolean_t b = B_TRUE;
1298 ptr += write_boolean(ptr, BUFLEN(lim, ptr), "transient", &b);
1299 }
1300
1301 /*
1302 * The daemon does not keep any active link attribute. Only store the
1303 * attributes if this request is for persistent configuration,
1304 */
1305 if (persist) {
1306 for (cur_p = linkp->ll_head; cur_p != NULL;
1307 cur_p = cur_p->lp_next) {
1308 ptr += translators[cur_p->lp_type].write_func(ptr,
1309 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1310 }
1311 }
1312
1313 if (ptr <= lim)
1314 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1315 }
1316
1317 int
1318 dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1319 {
1320 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
|