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.
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.
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]
23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
36 #include <sys/types.h>
43 #include <libcontract.h>
44 #include <libcontract_priv.h>
45 #include <sys/contract/process.h>
46 #include "dlmgmt_impl.h"
48 typedef enum dlmgmt_db_op
{
54 typedef struct dlmgmt_db_req_s
{
55 struct dlmgmt_db_req_s
*ls_next
;
57 char ls_link
[MAXLINKNAMELEN
];
58 datalink_id_t ls_linkid
;
60 uint32_t ls_flags
; /* Either DLMGMT_ACTIVE or */
61 /* DLMGMT_PERSIST, not both. */
65 * List of pending db updates (e.g., because of a read-only filesystem).
67 static dlmgmt_db_req_t
*dlmgmt_db_req_head
= NULL
;
68 static dlmgmt_db_req_t
*dlmgmt_db_req_tail
= NULL
;
71 * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
72 * line with an old format. This will cause the file being read to be
73 * re-written with the current format.
75 static boolean_t rewrite_needed
;
77 static int dlmgmt_db_update(dlmgmt_db_op_t
, const char *,
78 dlmgmt_link_t
*, uint32_t);
79 static int dlmgmt_process_db_req(dlmgmt_db_req_t
*);
80 static int dlmgmt_process_db_onereq(dlmgmt_db_req_t
*, boolean_t
);
81 static void *dlmgmt_db_update_thread(void *);
82 static boolean_t
process_link_line(char *, dlmgmt_link_t
*);
83 static int process_db_write(dlmgmt_db_req_t
*, FILE *, FILE *);
84 static int process_db_read(dlmgmt_db_req_t
*, FILE *);
85 static void generate_link_line(dlmgmt_link_t
*, boolean_t
, char *);
87 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
88 #define MAXLINELEN 1024
90 typedef void db_walk_func_t(dlmgmt_link_t
*);
93 * Translator functions to go from dladm_datatype_t to character strings.
94 * Each function takes a pointer to a buffer, the size of the buffer,
95 * the name of the attribute, and the value to be written. The functions
96 * return the number of bytes written to the buffer. If the buffer is not big
97 * enough to hold the string representing the value, then nothing is written
100 typedef size_t write_func_t(char *, size_t, char *, void *);
103 * Translator functions to read from a NULL terminated string buffer into
104 * something of the given DLADM_TYPE_*. The functions each return the number
105 * of bytes read from the string buffer. If there is an error reading data
106 * from the buffer, then 0 is returned. It is the caller's responsibility
107 * to free the data allocated by these functions.
109 typedef size_t read_func_t(char *, void **);
111 typedef struct translator_s
{
112 const char *type_name
;
113 write_func_t
*write_func
;
114 read_func_t
*read_func
;
118 * Translator functions, defined later but declared here so that
119 * the translator table can be defined.
121 static write_func_t write_str
, write_boolean
, write_uint64
;
122 static read_func_t read_str
, read_boolean
, read_int64
;
125 * Translator table, indexed by dladm_datatype_t.
127 static translator_t translators
[] = {
128 { "string", write_str
, read_str
},
129 { "boolean", write_boolean
, read_boolean
},
130 { "int", write_uint64
, read_int64
}
133 static size_t ntranslators
= sizeof (translators
) / sizeof (translator_t
);
135 #define LINK_PROPERTY_DELIMINATOR ";"
136 #define LINK_PROPERTY_TYPE_VALUE_SEP ","
137 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
138 strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
139 strlen(LINK_PROPERTY_DELIMINATOR) +\
141 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
142 (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
143 translators[(type)].type_name, \
144 LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
147 * Name of the cache file to keep the active <link name, linkid> mapping
149 char cachefile
[MAXPATHLEN
];
151 #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf"
152 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \
153 (void) snprintf((buffer), MAXPATHLEN, "%s", \
154 (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
156 typedef struct zopen_arg
{
157 const char *zopen_modestr
;
162 typedef struct zrename_arg
{
163 const char *zrename_newname
;
166 typedef union zfoparg
{
167 zopen_arg_t zfop_openarg
;
168 zrename_arg_t zfop_renamearg
;
171 typedef struct zfcbarg
{
172 boolean_t zfarg_inglobalzone
; /* is callback in global zone? */
173 zoneid_t zfarg_finglobalzone
; /* is file in global zone? */
174 const char *zfarg_filename
;
175 zfoparg_t
*zfarg_oparg
;
177 #define zfarg_openarg zfarg_oparg->zfop_openarg
178 #define zfarg_renamearg zfarg_oparg->zfop_renamearg
180 /* zone file callback */
181 typedef int zfcb_t(zfarg_t
*);
184 * Execute an operation on filename relative to zoneid's zone root. If the
185 * file is in the global zone, then the zfcb() callback will simply be called
186 * directly. If the file is in a non-global zone, then zfcb() will be called
187 * both from the global zone's context, and from the non-global zone's context
188 * (from a fork()'ed child that has entered the non-global zone). This is
189 * done to allow the callback to communicate with itself if needed (e.g. to
190 * pass back the file descriptor of an opened file).
193 dlmgmt_zfop(const char *filename
, zoneid_t zoneid
, zfcb_t
*zfcb
,
203 if (zoneid
!= GLOBAL_ZONEID
) {
205 * We need to access a file that isn't in the global zone.
206 * Accessing non-global zone files from the global zone is
207 * unsafe (due to symlink attacks), we'll need to fork a child
208 * that enters the zone in question and executes the callback
209 * that will operate on the file.
211 * Before we proceed with this zone tango, we need to create a
212 * new process contract for the child, as required by
216 ctfd
= open64("/system/contract/process/template", O_RDWR
);
219 if ((err
= ct_tmpl_set_critical(ctfd
, 0)) != 0 ||
220 (err
= ct_tmpl_set_informative(ctfd
, 0)) != 0 ||
221 (err
= ct_pr_tmpl_set_fatal(ctfd
, CT_PR_EV_HWERR
)) != 0 ||
222 (err
= ct_pr_tmpl_set_param(ctfd
, CT_PR_PGRPONLY
)) != 0 ||
223 (err
= ct_tmpl_activate(ctfd
)) != 0) {
230 (void) ct_tmpl_clear(ctfd
);
234 (void) ct_tmpl_clear(ctfd
);
237 * Elevate our privileges as zone_enter() requires all
240 if ((err
= dlmgmt_elevate_privileges()) != 0)
242 if (zone_enter(zoneid
) == -1)
244 if ((err
= dlmgmt_drop_privileges()) != 0)
248 if (contract_latest(&ct
) == -1)
250 (void) ct_tmpl_clear(ctfd
);
252 if (waitid(P_PID
, childpid
, &info
, WEXITED
) == -1) {
253 (void) contract_abandon_id(ct
);
256 (void) contract_abandon_id(ct
);
257 if (info
.si_status
!= 0)
258 return (info
.si_status
);
262 zfarg
.zfarg_inglobalzone
= (zoneid
== GLOBAL_ZONEID
|| childpid
!= 0);
263 zfarg
.zfarg_finglobalzone
= (zoneid
== GLOBAL_ZONEID
);
264 zfarg
.zfarg_filename
= filename
;
265 zfarg
.zfarg_oparg
= zfoparg
;
267 if (!zfarg
.zfarg_inglobalzone
)
273 dlmgmt_zopen_cb(zfarg_t
*zfarg
)
275 struct strrecvfd recvfd
;
276 boolean_t newfile
= B_FALSE
;
277 boolean_t inglobalzone
= zfarg
->zfarg_inglobalzone
;
278 zoneid_t finglobalzone
= zfarg
->zfarg_finglobalzone
;
279 const char *filename
= zfarg
->zfarg_filename
;
280 const char *modestr
= zfarg
->zfarg_openarg
.zopen_modestr
;
281 int *p
= zfarg
->zfarg_openarg
.zopen_pipe
;
288 /* We only ever open a file for reading or writing, not both. */
289 oflags
= (modestr
[0] == 'r') ? O_RDONLY
: O_WRONLY
| O_CREAT
| O_TRUNC
;
290 mode
= (modestr
[0] == 'r') ? 0 : S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
;
292 /* Open the file if we're in the same zone as the file. */
293 if (inglobalzone
== finglobalzone
) {
295 * First determine if we will be creating the file as part of
296 * opening it. If so, then we'll need to ensure that it has
297 * the proper ownership after having opened it.
299 if (oflags
& O_CREAT
) {
300 if (stat(filename
, &statbuf
) == -1) {
307 if ((fd
= open(filename
, oflags
, mode
)) == -1)
310 if (chown(filename
, UID_DLADM
, GID_NETADM
) == -1) {
319 * If we're not in the global zone, send the file-descriptor back to
320 * our parent in the global zone.
323 assert(!finglobalzone
);
325 return (ioctl(p
[1], I_SENDFD
, fd
) == -1 ? errno
: 0);
329 * At this point, we know we're in the global zone. If the file was
330 * in a non-global zone, receive the file-descriptor from our child in
331 * the non-global zone.
333 if (!finglobalzone
) {
334 if (ioctl(p
[0], I_RECVFD
, &recvfd
) == -1)
339 zfarg
->zfarg_openarg
.zopen_fd
= fd
;
344 dlmgmt_zunlink_cb(zfarg_t
*zfarg
)
346 if (zfarg
->zfarg_inglobalzone
!= zfarg
->zfarg_finglobalzone
)
348 return (unlink(zfarg
->zfarg_filename
) == 0 ? 0 : errno
);
352 dlmgmt_zrename_cb(zfarg_t
*zfarg
)
354 if (zfarg
->zfarg_inglobalzone
!= zfarg
->zfarg_finglobalzone
)
356 return (rename(zfarg
->zfarg_filename
,
357 zfarg
->zfarg_renamearg
.zrename_newname
) == 0 ? 0 : errno
);
361 * Same as fopen(3C), except that it opens the file relative to zoneid's zone
365 dlmgmt_zfopen(const char *filename
, const char *modestr
, zoneid_t zoneid
,
372 if (zoneid
!= GLOBAL_ZONEID
&& pipe(p
) == -1) {
377 zfoparg
.zfop_openarg
.zopen_modestr
= modestr
;
378 zfoparg
.zfop_openarg
.zopen_pipe
= p
;
379 *err
= dlmgmt_zfop(filename
, zoneid
, dlmgmt_zopen_cb
, &zfoparg
);
380 if (zoneid
!= GLOBAL_ZONEID
) {
385 fp
= fdopen(zfoparg
.zfop_openarg
.zopen_fd
, modestr
);
388 (void) close(zfoparg
.zfop_openarg
.zopen_fd
);
395 * Same as rename(2), except that old and new are relative to zoneid's zone
399 dlmgmt_zrename(const char *old
, const char *new, zoneid_t zoneid
)
403 zfoparg
.zfop_renamearg
.zrename_newname
= new;
404 return (dlmgmt_zfop(old
, zoneid
, dlmgmt_zrename_cb
, &zfoparg
));
408 * Same as unlink(2), except that filename is relative to zoneid's zone root.
411 dlmgmt_zunlink(const char *filename
, zoneid_t zoneid
)
413 return (dlmgmt_zfop(filename
, zoneid
, dlmgmt_zunlink_cb
, NULL
));
417 write_str(char *buffer
, size_t buffer_length
, char *name
, void *value
)
420 size_t data_length
= strnlen(ptr
, buffer_length
);
423 * Strings are assumed to be NULL terminated. In order to fit in
424 * the buffer, the string's length must be less then buffer_length.
425 * If the value is empty, there's no point in writing it, in fact,
426 * we shouldn't even see that case.
428 if (data_length
+ BASE_PROPERTY_LENGTH(DLADM_TYPE_STR
, name
) ==
429 buffer_length
|| data_length
== 0)
433 * Since we know the string will fit in the buffer, snprintf will
434 * always return less than buffer_length, so we can just return
435 * whatever snprintf returns.
437 return (GENERATE_PROPERTY_STRING(buffer
, buffer_length
, "%s",
438 name
, DLADM_TYPE_STR
, ptr
));
442 write_boolean(char *buffer
, size_t buffer_length
, char *name
, void *value
)
444 boolean_t
*ptr
= value
;
447 * Booleans are either zero or one, so we only need room for two
448 * characters in the buffer.
450 if (buffer_length
<= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN
, name
))
453 return (GENERATE_PROPERTY_STRING(buffer
, buffer_length
, "%d",
454 name
, DLADM_TYPE_BOOLEAN
, *ptr
));
458 write_uint64(char *buffer
, size_t buffer_length
, char *name
, void *value
)
460 uint64_t *ptr
= value
;
463 * Limit checking for uint64_t is a little trickier.
465 if (snprintf(NULL
, 0, "%lld", *ptr
) +
466 BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64
, name
) >= buffer_length
)
469 return (GENERATE_PROPERTY_STRING(buffer
, buffer_length
, "%lld",
470 name
, DLADM_TYPE_UINT64
, *ptr
));
474 read_str(char *buffer
, void **value
)
476 char *ptr
= calloc(MAXLINKATTRVALLEN
, sizeof (char));
479 if (ptr
== NULL
|| (len
= strlcpy(ptr
, buffer
, MAXLINKATTRVALLEN
))
480 >= MAXLINKATTRVALLEN
) {
485 *(char **)value
= ptr
;
487 /* Account for NULL terminator */
492 read_boolean(char *buffer
, void **value
)
494 boolean_t
*ptr
= calloc(1, sizeof (boolean_t
));
500 *(boolean_t
**)value
= ptr
;
502 return (sizeof (boolean_t
));
506 read_int64(char *buffer
, void **value
)
508 int64_t *ptr
= calloc(1, sizeof (int64_t));
513 *ptr
= (int64_t)atoll(buffer
);
514 *(int64_t **)value
= ptr
;
516 return (sizeof (int64_t));
519 static dlmgmt_db_req_t
*
520 dlmgmt_db_req_alloc(dlmgmt_db_op_t op
, const char *linkname
,
521 datalink_id_t linkid
, zoneid_t zoneid
, uint32_t flags
, int *err
)
523 dlmgmt_db_req_t
*req
;
525 if ((req
= calloc(1, sizeof (dlmgmt_db_req_t
))) == NULL
) {
529 if (linkname
!= NULL
)
530 (void) strlcpy(req
->ls_link
, linkname
, MAXLINKNAMELEN
);
531 req
->ls_linkid
= linkid
;
532 req
->ls_zoneid
= zoneid
;
533 req
->ls_flags
= flags
;
539 * Update the db entry with name "entryname" using information from "linkp".
542 dlmgmt_db_update(dlmgmt_db_op_t op
, const char *entryname
, dlmgmt_link_t
*linkp
,
545 dlmgmt_db_req_t
*req
;
548 /* It is either a persistent request or an active request, not both. */
549 assert((flags
== DLMGMT_PERSIST
) || (flags
== DLMGMT_ACTIVE
));
551 if ((req
= dlmgmt_db_req_alloc(op
, entryname
, linkp
->ll_linkid
,
552 linkp
->ll_zoneid
, flags
, &err
)) == NULL
)
556 * If the return error is EINPROGRESS, this request is handled
557 * asynchronously; return success.
559 err
= dlmgmt_process_db_req(req
);
560 if (err
!= EINPROGRESS
)
567 #define DLMGMT_DB_OP_STR(op) \
568 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
569 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
571 #define DLMGMT_DB_CONF_STR(flag) \
572 (((flag) == DLMGMT_ACTIVE) ? "active" : \
573 (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
576 dlmgmt_process_db_req(dlmgmt_db_req_t
*req
)
583 * If there are already pending "write" requests, queue this request in
584 * the pending list. Note that this function is called while the
585 * dlmgmt_rw_lock is held, so it is safe to access the global variables.
587 writeop
= (req
->ls_op
!= DLMGMT_DB_OP_READ
);
588 if (writeop
&& (req
->ls_flags
== DLMGMT_PERSIST
) &&
589 (dlmgmt_db_req_head
!= NULL
)) {
590 dlmgmt_db_req_tail
->ls_next
= req
;
591 dlmgmt_db_req_tail
= req
;
592 return (EINPROGRESS
);
595 err
= dlmgmt_process_db_onereq(req
, writeop
);
596 if (err
!= EINPROGRESS
&& err
!= 0 && err
!= ENOENT
) {
598 * Log the error unless the request processing is still in
599 * progress or if the configuration file hasn't been created
602 dlmgmt_log(LOG_WARNING
, "dlmgmt_process_db_onereq() %s "
603 "operation on %s configuration failed: %s",
604 DLMGMT_DB_OP_STR(req
->ls_op
),
605 DLMGMT_DB_CONF_STR(req
->ls_flags
), strerror(err
));
608 if (err
== EINPROGRESS
) {
609 assert(req
->ls_flags
== DLMGMT_PERSIST
);
610 assert(writeop
&& dlmgmt_db_req_head
== NULL
);
611 dlmgmt_db_req_tail
= dlmgmt_db_req_head
= req
;
612 err
= pthread_create(&tid
, NULL
, dlmgmt_db_update_thread
, NULL
);
614 return (EINPROGRESS
);
620 dlmgmt_process_db_onereq(dlmgmt_db_req_t
*req
, boolean_t writeop
)
623 FILE *fp
, *nfp
= NULL
;
624 char file
[MAXPATHLEN
];
625 char newfile
[MAXPATHLEN
];
627 DLMGMT_MAKE_FILE_DB_PATH(file
, (req
->ls_flags
== DLMGMT_PERSIST
));
628 fp
= dlmgmt_zfopen(file
, "r", req
->ls_zoneid
, &err
);
630 * Note that it is not an error if the file doesn't exist. If we're
631 * reading, we treat this case the same way as an empty file. If
632 * we're writing, the file will be created when we open the file for
635 if (fp
== NULL
&& !writeop
)
639 (void) snprintf(newfile
, MAXPATHLEN
, "%s.new", file
);
640 nfp
= dlmgmt_zfopen(newfile
, "w", req
->ls_zoneid
, &err
);
643 * EROFS can happen at boot when the file system is
644 * read-only. Return EINPROGRESS so that the caller
645 * can add this request to the pending request list
646 * and start a retry thread.
648 err
= (errno
== EROFS
? EINPROGRESS
: errno
);
653 if ((err
= process_db_write(req
, fp
, nfp
)) == 0)
654 err
= dlmgmt_zrename(newfile
, file
, req
->ls_zoneid
);
656 err
= process_db_read(req
, fp
);
663 (void) dlmgmt_zunlink(newfile
, req
->ls_zoneid
);
671 dlmgmt_db_update_thread(void *arg
)
673 dlmgmt_db_req_t
*req
;
675 dlmgmt_table_lock(B_TRUE
);
677 assert(dlmgmt_db_req_head
!= NULL
);
678 while ((req
= dlmgmt_db_req_head
) != NULL
) {
679 assert(req
->ls_flags
== DLMGMT_PERSIST
);
680 if (dlmgmt_process_db_onereq(req
, B_TRUE
) == EINPROGRESS
) {
682 * The filesystem is still read only. Go to sleep and
685 dlmgmt_table_unlock();
687 dlmgmt_table_lock(B_TRUE
);
692 * The filesystem is no longer read only. Continue processing
693 * and remove the request from the pending list.
695 dlmgmt_db_req_head
= req
->ls_next
;
696 if (dlmgmt_db_req_tail
== req
) {
697 assert(dlmgmt_db_req_head
== NULL
);
698 dlmgmt_db_req_tail
= NULL
;
703 dlmgmt_table_unlock();
708 parse_linkprops(char *buf
, dlmgmt_link_t
*linkp
)
710 boolean_t found_type
= B_FALSE
;
711 dladm_datatype_t type
= DLADM_TYPE_STR
;
714 char attr_name
[MAXLINKATTRLEN
];
715 size_t attr_buf_len
= 0;
716 void *attr_buf
= NULL
;
721 for (i
= 0; i
< len
; i
++) {
723 boolean_t match
= (c
== '=' ||
724 (c
== ',' && !found_type
) || c
== ';');
727 * Move to the next character if there is no match and
728 * if we have not reached the last character.
730 if (!match
&& i
!= len
- 1)
735 * NUL-terminate the string pointed to by 'curr'.
742 if (attr_name
[0] != '\0' && found_type
) {
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)
755 (datalink_class_t
)*(int64_t *)attr_buf
;
756 } else if (strcmp(attr_name
, "name") == 0) {
757 if (read_str(curr
, &attr_buf
) == 0)
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)
765 (datalink_class_t
)*(int64_t *)attr_buf
;
766 } else if (strcmp(attr_name
, "media") == 0) {
767 if (read_int64(curr
, &attr_buf
) == 0)
770 (uint32_t)*(int64_t *)attr_buf
;
772 attr_buf_len
= translators
[type
].read_func(curr
,
774 if (attr_buf_len
== 0)
777 if (linkattr_set(&(linkp
->ll_head
), attr_name
,
778 attr_buf
, attr_buf_len
, type
) != 0) {
786 found_type
= B_FALSE
;
787 } else if (attr_name
[0] != '\0') {
789 * Non-zero length attr_name and found_type of false
790 * indicates that we have not found the type for this
791 * attribute. The pattern now is "<type>,<val>;", we
792 * want the <type> part of the pattern.
794 for (type
= 0; type
< ntranslators
; type
++) {
796 translators
[type
].type_name
) == 0) {
806 * A zero length attr_name indicates we are looking
807 * at the beginning of a link attribute.
812 (void) snprintf(attr_name
, MAXLINKATTRLEN
, "%s", curr
);
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
;
827 * Free linkp->ll_head (link attribute list)
829 linkattr_destroy(linkp
);
834 process_link_line(char *buf
, dlmgmt_link_t
*linkp
)
838 char tmpbuf
[MAXLINELEN
];
840 bzero(linkp
, sizeof (*linkp
));
841 linkp
->ll_linkid
= DATALINK_INVALID_LINKID
;
844 * Use a copy of buf for parsing so that we can do whatever we want.
846 (void) strlcpy(tmpbuf
, buf
, MAXLINELEN
);
849 * Skip leading spaces, blank lines, and comments.
851 len
= strlen(tmpbuf
);
852 for (i
= 0; i
< len
; i
++) {
853 if (!isspace(tmpbuf
[i
]))
856 if (i
== len
|| tmpbuf
[i
] == '#')
861 * Find the link name and assign it to the link structure.
863 if (strtok_r(str
, " \n\t", &lasts
) == NULL
)
868 * Note that a previous version of the persistent datalink.conf file
869 * stored the linkid as the first field. In that case, the name will
870 * be obtained through parse_linkprops from a property with the format
871 * "name=<linkname>". If we encounter such a format, we set
872 * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
873 * the new format after it's done reading in the data.
875 if (isdigit(str
[0])) {
876 linkp
->ll_linkid
= atoi(str
);
877 rewrite_needed
= B_TRUE
;
879 if (strlcpy(linkp
->ll_link
, str
, sizeof (linkp
->ll_link
)) >=
880 sizeof (linkp
->ll_link
))
885 if (str
>= tmpbuf
+ len
)
889 * Now find the list of link properties.
891 if ((str
= strtok_r(str
, " \n\t", &lasts
)) == NULL
)
894 if (parse_linkprops(str
, linkp
) < 0)
901 * Delete corrupted line.
908 * Find any properties in linkp that refer to "old", and rename to "new".
909 * Return B_TRUE if any renaming occurred.
912 dlmgmt_attr_rename(dlmgmt_link_t
*linkp
, const char *old
, const char *new,
915 dlmgmt_linkattr_t
*attrp
;
916 char *newval
= NULL
, *pname
;
917 char valcp
[MAXLINKATTRVALLEN
];
922 if ((attrp
= linkattr_find(linkp
->ll_head
, "linkover")) != NULL
||
923 (attrp
= linkattr_find(linkp
->ll_head
, "simnetpeer")) != NULL
) {
924 if (strcmp(old
, (char *)attrp
->lp_val
) == 0) {
925 newsize
= strlen(new) + 1;
926 if ((newval
= malloc(newsize
)) == NULL
)
928 (void) strcpy(newval
, new);
930 attrp
->lp_val
= newval
;
931 attrp
->lp_sz
= newsize
;
937 if ((attrp
= linkattr_find(linkp
->ll_head
, "portnames")) == NULL
)
940 /* <linkname>:[<linkname>:]... */
941 if ((newval
= calloc(MAXLINKATTRVALLEN
, sizeof (char))) == NULL
)
944 bcopy(attrp
->lp_val
, valcp
, sizeof (valcp
));
945 pname
= strtok(valcp
, ":");
946 while (pname
!= NULL
) {
947 if (strcmp(pname
, old
) == 0) {
948 (void) strcat(newval
, new);
951 (void) strcat(newval
, pname
);
953 (void) strcat(newval
, ":");
954 pname
= strtok(NULL
, ":");
958 attrp
->lp_val
= newval
;
959 attrp
->lp_sz
= strlen(newval
) + 1;
967 process_db_write(dlmgmt_db_req_t
*req
, FILE *fp
, FILE *nfp
)
969 boolean_t done
= B_FALSE
;
971 dlmgmt_link_t link_in_file
, *linkp
= NULL
, *dblinkp
;
972 boolean_t persist
= (req
->ls_flags
== DLMGMT_PERSIST
);
973 boolean_t writeall
, rename
, attr_renamed
;
974 char buf
[MAXLINELEN
];
976 writeall
= (req
->ls_linkid
== DATALINK_ALL_LINKID
);
978 if (req
->ls_op
== DLMGMT_DB_OP_WRITE
&& !writeall
) {
980 * find the link in the avl tree with the given linkid.
982 linkp
= link_by_id(req
->ls_linkid
, req
->ls_zoneid
);
983 if (linkp
== NULL
|| (linkp
->ll_flags
& req
->ls_flags
) == 0) {
985 * This link has already been changed. This could
986 * happen if the request is pending because of
987 * read-only file-system. If so, we are done.
992 * In the case of a rename, linkp's name has been updated to
993 * the new name, and req->ls_link is the old link name.
995 rename
= (strcmp(req
->ls_link
, linkp
->ll_link
) != 0);
999 * fp can be NULL if the file didn't initially exist and we're
1000 * creating it as part of this write operation.
1005 while (err
== 0 && fgets(buf
, sizeof (buf
), fp
) != NULL
&&
1006 process_link_line(buf
, &link_in_file
)) {
1008 * Only the link name is needed. Free the memory allocated for
1009 * the link attributes list of link_in_file.
1011 linkattr_destroy(&link_in_file
);
1013 if (link_in_file
.ll_link
[0] == '\0' || done
) {
1015 * this is a comment line or we are done updating the
1016 * line for the specified link, write the rest of
1019 if (fputs(buf
, nfp
) == EOF
)
1024 switch (req
->ls_op
) {
1025 case DLMGMT_DB_OP_WRITE
:
1027 * For write operations, we generate a new output line
1028 * if we're either writing all links (writeall) or if
1029 * the name of the link in the file matches the one
1030 * we're looking for. Otherwise, we write out the
1033 * If we're doing a rename operation, ensure that any
1034 * references to the link being renamed in link
1035 * properties are also updated before we write
1039 linkp
= link_by_name(link_in_file
.ll_link
,
1042 if (writeall
|| strcmp(req
->ls_link
,
1043 link_in_file
.ll_link
) == 0) {
1044 generate_link_line(linkp
, persist
, buf
);
1045 if (!writeall
&& !rename
)
1047 } else if (rename
&& persist
) {
1048 dblinkp
= link_by_name(link_in_file
.ll_link
,
1050 err
= dlmgmt_attr_rename(dblinkp
, req
->ls_link
,
1051 linkp
->ll_link
, &attr_renamed
);
1055 generate_link_line(dblinkp
, persist
,
1059 if (fputs(buf
, nfp
) == EOF
)
1062 case DLMGMT_DB_OP_DELETE
:
1064 * Delete is simple. If buf does not represent the
1065 * link we're deleting, write it out.
1067 if (strcmp(req
->ls_link
, link_in_file
.ll_link
) != 0) {
1068 if (fputs(buf
, nfp
) == EOF
)
1074 case DLMGMT_DB_OP_READ
:
1083 * If we get to the end of the file and have not seen what linkid
1084 * points to, write it out then.
1086 if (req
->ls_op
== DLMGMT_DB_OP_WRITE
&& !writeall
&& !rename
&& !done
) {
1087 generate_link_line(linkp
, persist
, buf
);
1089 if (fputs(buf
, nfp
) == EOF
)
1097 process_db_read(dlmgmt_db_req_t
*req
, FILE *fp
)
1099 avl_index_t name_where
, id_where
;
1100 dlmgmt_link_t link_in_file
, *newlink
, *link_in_db
;
1101 char buf
[MAXLINELEN
];
1105 * This loop processes each line of the configuration file.
1107 while (fgets(buf
, MAXLINELEN
, fp
) != NULL
) {
1108 if (!process_link_line(buf
, &link_in_file
)) {
1114 * Skip the comment line.
1116 if (link_in_file
.ll_link
[0] == '\0') {
1117 linkattr_destroy(&link_in_file
);
1121 if ((req
->ls_flags
& DLMGMT_ACTIVE
) &&
1122 link_in_file
.ll_linkid
== DATALINK_INVALID_LINKID
) {
1123 linkattr_destroy(&link_in_file
);
1127 link_in_file
.ll_zoneid
= req
->ls_zoneid
;
1128 link_in_db
= link_by_name(link_in_file
.ll_link
,
1129 link_in_file
.ll_zoneid
);
1130 if (link_in_db
!= NULL
) {
1132 * If the link in the database already has the flag
1133 * for this request set, then the entry is a
1134 * duplicate. If it's not a duplicate, then simply
1135 * turn on the appropriate flag on the existing link.
1137 if (link_in_db
->ll_flags
& req
->ls_flags
) {
1138 dlmgmt_log(LOG_WARNING
, "Duplicate links "
1139 "in the repository: %s",
1140 link_in_file
.ll_link
);
1141 linkattr_destroy(&link_in_file
);
1143 if (req
->ls_flags
& DLMGMT_PERSIST
) {
1145 * Save the newly read properties into
1146 * the existing link.
1148 assert(link_in_db
->ll_head
== NULL
);
1149 link_in_db
->ll_head
=
1150 link_in_file
.ll_head
;
1152 linkattr_destroy(&link_in_file
);
1154 link_in_db
->ll_flags
|= req
->ls_flags
;
1158 * This is a new link. Allocate a new dlmgmt_link_t
1159 * and add it to the trees.
1161 newlink
= calloc(1, sizeof (*newlink
));
1162 if (newlink
== NULL
) {
1163 dlmgmt_log(LOG_WARNING
, "Unable to allocate "
1164 "memory to create new link %s",
1165 link_in_file
.ll_link
);
1166 linkattr_destroy(&link_in_file
);
1169 bcopy(&link_in_file
, newlink
, sizeof (*newlink
));
1171 if (newlink
->ll_linkid
== DATALINK_INVALID_LINKID
)
1172 newlink
->ll_linkid
= dlmgmt_nextlinkid
;
1173 if (avl_find(&dlmgmt_id_avl
, newlink
, &id_where
) !=
1175 dlmgmt_log(LOG_WARNING
, "Link ID %d is already"
1176 " in use, destroying link %s",
1177 newlink
->ll_linkid
, newlink
->ll_link
);
1178 link_destroy(newlink
);
1182 if ((req
->ls_flags
& DLMGMT_ACTIVE
) &&
1183 link_activate(newlink
) != 0) {
1184 dlmgmt_log(LOG_WARNING
, "Unable to activate %s",
1186 link_destroy(newlink
);
1190 avl_insert(&dlmgmt_id_avl
, newlink
, id_where
);
1192 * link_activate call above can insert newlink in
1193 * dlmgmt_name_avl tree when activating a link that is
1194 * assigned to a NGZ.
1196 if (avl_find(&dlmgmt_name_avl
, newlink
,
1197 &name_where
) == NULL
)
1198 avl_insert(&dlmgmt_name_avl
, newlink
,
1201 dlmgmt_advance(newlink
);
1202 newlink
->ll_flags
|= req
->ls_flags
;
1210 * Generate an entry in the link database.
1211 * Each entry has this format:
1212 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1215 generate_link_line(dlmgmt_link_t
*linkp
, boolean_t persist
, char *buf
)
1217 char tmpbuf
[MAXLINELEN
];
1219 char *lim
= tmpbuf
+ MAXLINELEN
;
1220 dlmgmt_linkattr_t
*cur_p
= NULL
;
1223 ptr
+= snprintf(ptr
, BUFLEN(lim
, ptr
), "%s\t", linkp
->ll_link
);
1226 * We store the linkid in the active database so that dlmgmtd
1227 * can recover in the event that it is restarted.
1229 u64
= linkp
->ll_linkid
;
1230 ptr
+= write_uint64(ptr
, BUFLEN(lim
, ptr
), "linkid", &u64
);
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
);
1238 * The daemon does not keep any active link attribute. Only store the
1239 * attributes if this request is for persistent configuration,
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
);
1250 (void) snprintf(buf
, MAXLINELEN
, "%s\n", tmpbuf
);
1254 dlmgmt_delete_db_entry(dlmgmt_link_t
*linkp
, uint32_t flags
)
1256 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE
, linkp
->ll_link
, linkp
,
1261 dlmgmt_write_db_entry(const char *entryname
, dlmgmt_link_t
*linkp
,
1266 if (flags
& DLMGMT_PERSIST
) {
1267 if ((err
= dlmgmt_db_update(DLMGMT_DB_OP_WRITE
, entryname
,
1268 linkp
, DLMGMT_PERSIST
)) != 0) {
1273 if (flags
& DLMGMT_ACTIVE
) {
1274 if (((err
= dlmgmt_db_update(DLMGMT_DB_OP_WRITE
, entryname
,
1275 linkp
, DLMGMT_ACTIVE
)) != 0) && (flags
& DLMGMT_PERSIST
)) {
1276 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE
, entryname
,
1277 linkp
, DLMGMT_PERSIST
);
1286 * Upgrade properties that have link IDs as values to link names. Because '.'
1287 * is a valid linkname character, the port separater for link aggregations
1288 * must be changed to ':'.
1291 linkattr_upgrade(dlmgmt_linkattr_t
*attrp
)
1293 datalink_id_t linkid
;
1295 char portname
[MAXLINKNAMELEN
+ 1];
1296 dlmgmt_link_t
*linkp
;
1299 boolean_t upgraded
= B_FALSE
;
1301 if (strcmp(attrp
->lp_name
, "linkover") == 0 ||
1302 strcmp(attrp
->lp_name
, "simnetpeer") == 0) {
1303 if (attrp
->lp_type
== DLADM_TYPE_UINT64
) {
1304 linkid
= (datalink_id_t
)*(uint64_t *)attrp
->lp_val
;
1305 if ((linkp
= link_by_id(linkid
, GLOBAL_ZONEID
)) == NULL
)
1307 new_attr_sz
= strlen(linkp
->ll_link
) + 1;
1308 if ((new_attr_val
= malloc(new_attr_sz
)) == NULL
)
1310 (void) strcpy(new_attr_val
, linkp
->ll_link
);
1313 } else if (strcmp(attrp
->lp_name
, "portnames") == 0) {
1315 * The old format for "portnames" was
1316 * "<linkid>.[<linkid>.]...". The new format is
1317 * "<linkname>:[<linkname>:]...".
1319 if (!isdigit(((char *)attrp
->lp_val
)[0]))
1321 new_attr_val
= calloc(MAXLINKATTRVALLEN
, sizeof (char));
1322 if (new_attr_val
== NULL
)
1324 portidstr
= (char *)attrp
->lp_val
;
1325 while (*portidstr
!= '\0') {
1327 linkid
= strtol(portidstr
, &portidstr
, 10);
1328 if (linkid
== 0 || *portidstr
!= '.' ||
1329 (linkp
= link_by_id(linkid
, GLOBAL_ZONEID
)) ==
1334 (void) snprintf(portname
, sizeof (portname
), "%s:",
1336 if (strlcat(new_attr_val
, portname
,
1337 MAXLINKATTRVALLEN
) >= MAXLINKATTRVALLEN
) {
1341 /* skip the '.' delimiter */
1344 new_attr_sz
= strlen(new_attr_val
) + 1;
1349 attrp
->lp_type
= DLADM_TYPE_STR
;
1350 attrp
->lp_sz
= new_attr_sz
;
1351 free(attrp
->lp_val
);
1352 attrp
->lp_val
= new_attr_val
;
1357 dlmgmt_db_upgrade(dlmgmt_link_t
*linkp
)
1359 dlmgmt_linkattr_t
*attrp
;
1361 for (attrp
= linkp
->ll_head
; attrp
!= NULL
; attrp
= attrp
->lp_next
)
1362 linkattr_upgrade(attrp
);
1366 dlmgmt_db_phys_activate(dlmgmt_link_t
*linkp
)
1368 linkp
->ll_flags
|= DLMGMT_ACTIVE
;
1369 (void) dlmgmt_write_db_entry(linkp
->ll_link
, linkp
, DLMGMT_ACTIVE
);
1373 dlmgmt_db_walk(zoneid_t zoneid
, datalink_class_t
class, db_walk_func_t
*func
)
1375 dlmgmt_link_t
*linkp
;
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))
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.
1391 dlmgmt_db_init(zoneid_t zoneid
)
1393 dlmgmt_db_req_t
*req
;
1395 boolean_t boot
= B_FALSE
;
1397 if ((req
= dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ
, NULL
,
1398 DATALINK_INVALID_LINKID
, zoneid
, DLMGMT_ACTIVE
, &err
)) == NULL
)
1401 if ((err
= dlmgmt_process_db_req(req
)) != 0) {
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.
1413 req
->ls_flags
= DLMGMT_PERSIST
;
1414 err
= dlmgmt_process_db_req(req
);
1415 if (err
!= 0 && err
!= ENOENT
)
1418 if (rewrite_needed
) {
1420 * First update links in memory, then dump the entire db to
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 &&
1431 dlmgmt_db_walk(zoneid
, DATALINK_CLASS_PHYS
,
1432 dlmgmt_db_phys_activate
);
1436 if (err
== EINPROGRESS
)
1444 * Remove all links in the given zoneid.
1447 dlmgmt_db_fini(zoneid_t zoneid
)
1449 dlmgmt_link_t
*linkp
= avl_first(&dlmgmt_name_avl
), *next_linkp
;
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
);