Print this page
post-merge correct dlmgmt_db.c
15167 Panic when halting a zone with self-created links
15407 zone_free asserts to its destruction when dlmgmtd has fallen
15408 dlmgmtd/zoneadmd interdependencies prevent dlmgmtd from starting
15409 dlmgmtd forking with write locks results in deadlock chaos
Portions contributed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Portions contributed by: Joshua M. Clulow <jmc@joyent.com>
Portions contributed by: Robert Mustacchi <rm@joyent.com>
Portions contributed by: Ryan Zezeski <rpz@joyent.com>
Change-ID: I17810387d2142c5c7e426e106e4c2dbb8f2496d4
Conflicts:
    usr/src/cmd/dlmgmtd/dlmgmt_db.c
    usr/src/cmd/dlmgmtd/dlmgmt_door.c
    usr/src/cmd/dlmgmtd/dlmgmt_impl.h
    usr/src/cmd/dlmgmtd/dlmgmt_main.c
    usr/src/cmd/dlmgmtd/dlmgmt_util.c
    usr/src/cmd/zoneadmd/vplat.c
    usr/src/lib/libdladm/common/libdladm.h
    usr/src/lib/libdladm/common/libdlmgmt.c
    usr/src/uts/common/io/dls/dls_mgmt.c


   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,