xref: /illumos-gate/usr/src/lib/libdladm/common/libdladm.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <unistd.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <strings.h>
31 #include <dirent.h>
32 #include <stdlib.h>
33 #include <sys/param.h>
34 #include <sys/stat.h>
35 #include <sys/dld.h>
36 #include <sys/dld_ioc.h>
37 #include <libdladm_impl.h>
38 #include <libintl.h>
39 #include <libdlpi.h>
40 
41 static char	dladm_rootdir[MAXPATHLEN] = "/";
42 
43 typedef struct media_type_desc {
44 	uint32_t	media_type;
45 #define	MAX_MEDIA_TYPE_STRING	32
46 	const char	media_type_str[MAX_MEDIA_TYPE_STRING];
47 } media_type_t;
48 
49 static media_type_t media_type_table[] =  {
50 	{ DL_ETHER,	"Ethernet" },
51 	{ DL_WIFI,	"WiFi" },
52 	{ DL_IB,	"Infiniband" },
53 	{ DL_IPV4,	"IPv4Tunnel" },
54 	{ DL_IPV6,	"IPv6Tunnel" },
55 	{ DL_6TO4,	"6to4Tunnel" },
56 	{ DL_CSMACD,	"CSMA/CD" },
57 	{ DL_TPB,	"TokenBus" },
58 	{ DL_TPR,	"TokenRing" },
59 	{ DL_METRO,	"MetroNet" },
60 	{ DL_HDLC,	"HDLC" },
61 	{ DL_CHAR,	"SyncCharacter" },
62 	{ DL_CTCA,	"CTCA" },
63 	{ DL_FDDI, 	"FDDI" },
64 	{ DL_FC, 	"FiberChannel" },
65 	{ DL_ATM, 	"ATM" },
66 	{ DL_IPATM, 	"ATM(ClassicIP)" },
67 	{ DL_X25, 	"X.25" },
68 	{ DL_IPX25, 	"X.25(ClassicIP)" },
69 	{ DL_ISDN, 	"ISDN" },
70 	{ DL_HIPPI, 	"HIPPI" },
71 	{ DL_100VG, 	"100BaseVGEthernet" },
72 	{ DL_100VGTPR, 	"100BaseVGTokenRing" },
73 	{ DL_ETH_CSMA, 	"IEEE802.3" },
74 	{ DL_100BT, 	"100BaseT" },
75 	{ DL_FRAME, 	"FrameRelay" },
76 	{ DL_MPFRAME, 	"MPFrameRelay" },
77 	{ DL_ASYNC, 	"AsyncCharacter" },
78 	{ DL_IPNET, 	"IPNET" },
79 	{ DL_OTHER, 	"Other" }
80 };
81 #define	MEDIATYPECOUNT	(sizeof (media_type_table) / sizeof (media_type_t))
82 
83 dladm_status_t
84 dladm_open(dladm_handle_t *handle)
85 {
86 	int dld_fd;
87 
88 	if (handle == NULL)
89 		return (DLADM_STATUS_BADARG);
90 
91 	if ((dld_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
92 		return (dladm_errno2status(errno));
93 
94 	/*
95 	 * Don't open DLMGMT_DOOR now.  dlmgmtd(1M) is not able to
96 	 * open the door when the dladm handle is opened because the
97 	 * door hasn't been created yet at that time.  Thus, we must
98 	 * open it on-demand in dladm_door_fd().  Move the open()
99 	 * to dladm_door_fd() for all cases.
100 	 */
101 
102 	if ((*handle = malloc(sizeof (struct dladm_handle))) == NULL) {
103 		(void) close(dld_fd);
104 		return (DLADM_STATUS_NOMEM);
105 	}
106 
107 	(*handle)->dld_fd = dld_fd;
108 	(*handle)->door_fd = -1;
109 
110 	return (DLADM_STATUS_OK);
111 }
112 
113 void
114 dladm_close(dladm_handle_t handle)
115 {
116 	if (handle != NULL) {
117 		(void) close(handle->dld_fd);
118 		if (handle->door_fd != -1)
119 			(void) close(handle->door_fd);
120 		free(handle);
121 	}
122 }
123 
124 int
125 dladm_dld_fd(dladm_handle_t handle)
126 {
127 	return (handle->dld_fd);
128 }
129 
130 /*
131  * If DLMGMT_DOOR hasn't been opened in the handle yet, open it.
132  */
133 dladm_status_t
134 dladm_door_fd(dladm_handle_t handle, int *door_fd)
135 {
136 	int fd;
137 
138 	if (handle->door_fd == -1) {
139 		if ((fd = open(DLMGMT_DOOR, O_RDONLY)) < 0)
140 			return (dladm_errno2status(errno));
141 		handle->door_fd = fd;
142 	}
143 	*door_fd = handle->door_fd;
144 
145 	return (DLADM_STATUS_OK);
146 }
147 
148 const char *
149 dladm_status2str(dladm_status_t status, char *buf)
150 {
151 	const char	*s;
152 
153 	switch (status) {
154 	case DLADM_STATUS_OK:
155 		s = "ok";
156 		break;
157 	case DLADM_STATUS_BADARG:
158 		s = "invalid argument";
159 		break;
160 	case DLADM_STATUS_FAILED:
161 		s = "operation failed";
162 		break;
163 	case DLADM_STATUS_TOOSMALL:
164 		s = "buffer size too small";
165 		break;
166 	case DLADM_STATUS_NOTSUP:
167 		s = "operation not supported";
168 		break;
169 	case DLADM_STATUS_NOTFOUND:
170 		s = "object not found";
171 		break;
172 	case DLADM_STATUS_BADVAL:
173 		s = "invalid value";
174 		break;
175 	case DLADM_STATUS_NOMEM:
176 		s = "insufficient memory";
177 		break;
178 	case DLADM_STATUS_EXIST:
179 		s = "object already exists";
180 		break;
181 	case DLADM_STATUS_LINKINVAL:
182 		s = "invalid link";
183 		break;
184 	case DLADM_STATUS_PROPRDONLY:
185 		s = "read-only property";
186 		break;
187 	case DLADM_STATUS_BADVALCNT:
188 		s = "invalid number of values";
189 		break;
190 	case DLADM_STATUS_DBNOTFOUND:
191 		s = "database not found";
192 		break;
193 	case DLADM_STATUS_DENIED:
194 		s = "permission denied";
195 		break;
196 	case DLADM_STATUS_IOERR:
197 		s = "I/O error";
198 		break;
199 	case DLADM_STATUS_TEMPONLY:
200 		s = "change cannot be persistent";
201 		break;
202 	case DLADM_STATUS_TIMEDOUT:
203 		s = "operation timed out";
204 		break;
205 	case DLADM_STATUS_ISCONN:
206 		s = "already connected";
207 		break;
208 	case DLADM_STATUS_NOTCONN:
209 		s = "not connected";
210 		break;
211 	case DLADM_STATUS_REPOSITORYINVAL:
212 		s = "invalid configuration repository";
213 		break;
214 	case DLADM_STATUS_MACADDRINVAL:
215 		s = "invalid MAC address";
216 		break;
217 	case DLADM_STATUS_KEYINVAL:
218 		s = "invalid key";
219 		break;
220 	case DLADM_STATUS_INVALIDMACADDRLEN:
221 		s = "invalid MAC address length";
222 		break;
223 	case DLADM_STATUS_INVALIDMACADDRTYPE:
224 		s = "invalid MAC address type";
225 		break;
226 	case DLADM_STATUS_LINKBUSY:
227 		s = "link busy";
228 		break;
229 	case DLADM_STATUS_VIDINVAL:
230 		s = "invalid VLAN identifier";
231 		break;
232 	case DLADM_STATUS_TRYAGAIN:
233 		s = "try again later";
234 		break;
235 	case DLADM_STATUS_NONOTIF:
236 		s = "link notification is not supported";
237 		break;
238 	case DLADM_STATUS_BADTIMEVAL:
239 		s = "invalid time range";
240 		break;
241 	case DLADM_STATUS_INVALIDMACADDR:
242 		s = "invalid MAC address value";
243 		break;
244 	case DLADM_STATUS_INVALIDMACADDRNIC:
245 		s = "MAC address reserved for use by underlying data-link";
246 		break;
247 	case DLADM_STATUS_INVALIDMACADDRINUSE:
248 		s = "MAC address is already in use";
249 		break;
250 	case DLADM_STATUS_MACFACTORYSLOTINVALID:
251 		s = "invalid factory MAC address slot";
252 		break;
253 	case DLADM_STATUS_MACFACTORYSLOTUSED:
254 		s = "factory MAC address slot already used";
255 		break;
256 	case DLADM_STATUS_MACFACTORYSLOTALLUSED:
257 		s = "all factory MAC address slots are in use";
258 		break;
259 	case DLADM_STATUS_MACFACTORYNOTSUP:
260 		s = "factory MAC address slots not supported";
261 		break;
262 	case DLADM_STATUS_INVALIDMACPREFIX:
263 		s = "Invalid MAC address prefix value";
264 		break;
265 	case DLADM_STATUS_INVALIDMACPREFIXLEN:
266 		s = "Invalid MAC address prefix length";
267 		break;
268 	case DLADM_STATUS_CPUMAX:
269 		s = "non-existent processor ID";
270 		break;
271 	case DLADM_STATUS_CPUERR:
272 		s = "could not determine processor status";
273 		break;
274 	case DLADM_STATUS_CPUNOTONLINE:
275 		s = "processor not online";
276 		break;
277 	case DLADM_STATUS_DB_NOTFOUND:
278 		s = "database not found";
279 		break;
280 	case DLADM_STATUS_DB_PARSE_ERR:
281 		s = "database parse error";
282 		break;
283 	case DLADM_STATUS_PROP_PARSE_ERR:
284 		s = "property parse error";
285 		break;
286 	case DLADM_STATUS_ATTR_PARSE_ERR:
287 		s = "attribute parse error";
288 		break;
289 	case DLADM_STATUS_FLOW_DB_ERR:
290 		s = "flow database error";
291 		break;
292 	case DLADM_STATUS_FLOW_DB_OPEN_ERR:
293 		s = "flow database open error";
294 		break;
295 	case DLADM_STATUS_FLOW_DB_PARSE_ERR:
296 		s = "flow database parse error";
297 		break;
298 	case DLADM_STATUS_FLOWPROP_DB_PARSE_ERR:
299 		s = "flow property database parse error";
300 		break;
301 	case DLADM_STATUS_FLOW_ADD_ERR:
302 		s = "flow add error";
303 		break;
304 	case DLADM_STATUS_FLOW_WALK_ERR:
305 		s = "flow walk error";
306 		break;
307 	case DLADM_STATUS_FLOW_IDENTICAL:
308 		s = "a flow with identical attributes exists";
309 		break;
310 	case DLADM_STATUS_FLOW_INCOMPATIBLE:
311 		s = "flow(s) with incompatible attributes exists";
312 		break;
313 	case DLADM_STATUS_FLOW_EXISTS:
314 		s = "link still has flows";
315 		break;
316 	case DLADM_STATUS_PERSIST_FLOW_EXISTS:
317 		s = "persistent flow with the same name exists";
318 		break;
319 	case DLADM_STATUS_INVALID_IP:
320 		s = "invalid IP address";
321 		break;
322 	case DLADM_STATUS_INVALID_PREFIXLEN:
323 		s = "invalid IP prefix length";
324 		break;
325 	case DLADM_STATUS_INVALID_PROTOCOL:
326 		s = "invalid IP protocol";
327 		break;
328 	case DLADM_STATUS_INVALID_PORT:
329 		s = "invalid port number";
330 		break;
331 	case DLADM_STATUS_INVALID_DSF:
332 		s = "invalid dsfield";
333 		break;
334 	case DLADM_STATUS_INVALID_DSFMASK:
335 		s = "invalid dsfield mask";
336 		break;
337 	case DLADM_STATUS_INVALID_MACMARGIN:
338 		s = "MTU check failed, use lower MTU or -f option";
339 		break;
340 	case DLADM_STATUS_BADPROP:
341 		s = "invalid property";
342 		break;
343 	case DLADM_STATUS_MINMAXBW:
344 		s = "minimum value for maxbw is 1.2M";
345 		break;
346 	case DLADM_STATUS_NO_HWRINGS:
347 		s = "request hw rings failed";
348 		break;
349 	case DLADM_STATUS_PERMONLY:
350 		s = "change must be persistent";
351 		break;
352 	case DLADM_STATUS_OPTMISSING:
353 		s = "optional software not installed";
354 		break;
355 	case DLADM_STATUS_IPTUNTYPE:
356 		s = "invalid IP tunnel type";
357 		break;
358 	case DLADM_STATUS_IPTUNTYPEREQD:
359 		s = "IP tunnel type required";
360 		break;
361 	case DLADM_STATUS_BADIPTUNLADDR:
362 		s = "invalid local IP tunnel address";
363 		break;
364 	case DLADM_STATUS_BADIPTUNRADDR:
365 		s = "invalid remote IP tunnel address";
366 		break;
367 	case DLADM_STATUS_ADDRINUSE:
368 		s = "address already in use";
369 		break;
370 	default:
371 		s = "<unknown error>";
372 		break;
373 	}
374 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
375 	return (buf);
376 }
377 
378 /*
379  * Convert a unix errno to a dladm_status_t.
380  * We only convert errnos that are likely to be encountered. All others
381  * are mapped to DLADM_STATUS_FAILED.
382  */
383 dladm_status_t
384 dladm_errno2status(int err)
385 {
386 	switch (err) {
387 	case 0:
388 		return (DLADM_STATUS_OK);
389 	case EINVAL:
390 		return (DLADM_STATUS_BADARG);
391 	case EEXIST:
392 		return (DLADM_STATUS_EXIST);
393 	case ENOENT:
394 		return (DLADM_STATUS_NOTFOUND);
395 	case ENOSPC:
396 		return (DLADM_STATUS_TOOSMALL);
397 	case ENOMEM:
398 		return (DLADM_STATUS_NOMEM);
399 	case ENOTSUP:
400 		return (DLADM_STATUS_NOTSUP);
401 	case ENETDOWN:
402 		return (DLADM_STATUS_NONOTIF);
403 	case EACCES:
404 	case EPERM:
405 		return (DLADM_STATUS_DENIED);
406 	case EIO:
407 		return (DLADM_STATUS_IOERR);
408 	case EBUSY:
409 		return (DLADM_STATUS_LINKBUSY);
410 	case EAGAIN:
411 		return (DLADM_STATUS_TRYAGAIN);
412 	case ENOTEMPTY:
413 		return (DLADM_STATUS_FLOW_EXISTS);
414 	case EOPNOTSUPP:
415 		return (DLADM_STATUS_FLOW_INCOMPATIBLE);
416 	case EALREADY:
417 		return (DLADM_STATUS_FLOW_IDENTICAL);
418 	case EADDRINUSE:
419 		return (DLADM_STATUS_ADDRINUSE);
420 	default:
421 		return (DLADM_STATUS_FAILED);
422 	}
423 }
424 
425 boolean_t
426 dladm_str2interval(char *oarg, uint32_t *interval)
427 {
428 	int		val;
429 	char		*endp = NULL;
430 
431 	errno = 0;
432 	val = strtol(oarg, &endp, 10);
433 	if (errno != 0 || val <= 0 || *endp != '\0')
434 		return (B_FALSE);
435 
436 	*interval = val;
437 
438 	return (B_TRUE);
439 }
440 
441 dladm_status_t
442 dladm_str2bw(char *oarg, uint64_t *bw)
443 {
444 	char		*endp = NULL;
445 	int64_t		n;
446 	int		mult = 1;
447 
448 	n = strtoull(oarg, &endp, 10);
449 
450 	if ((errno != 0) || (strlen(endp) > 1))
451 		return (DLADM_STATUS_BADARG);
452 
453 	if (n < 0)
454 		return (DLADM_STATUS_BADVAL);
455 
456 	switch (*endp) {
457 	case 'k':
458 	case 'K':
459 		mult = 1000;
460 		break;
461 	case 'm':
462 	case 'M':
463 	case '\0':
464 		mult = 1000000;
465 		break;
466 	case 'g':
467 	case 'G':
468 		mult = 1000000000;
469 		break;
470 	case '%':
471 		/*
472 		 * percentages not supported for now,
473 		 * see RFE 6540675
474 		 */
475 		return (DLADM_STATUS_NOTSUP);
476 	default:
477 		return (DLADM_STATUS_BADVAL);
478 	}
479 
480 	*bw = n * mult;
481 
482 	/* check for overflow */
483 	if (*bw / mult != n)
484 		return (DLADM_STATUS_BADARG);
485 
486 	return (DLADM_STATUS_OK);
487 }
488 
489 /*
490  * Convert bandwidth in bps to a string in mpbs.  For values greater
491  * than 1mbps or 1000000, print a whole mbps value.  For values that
492  * have fractional Mbps in whole Kbps , print the bandwidth in a manner
493  * simlilar to a floating point format.
494  *
495  *        bps       string
496  *          0            0
497  *        100            0
498  *       2000        0.002
499  *     431000        0.431
500  *    1000000            1
501  *    1030000        1.030
502  *  100000000          100
503  */
504 const char *
505 dladm_bw2str(int64_t bw, char *buf)
506 {
507 	int kbps, mbps;
508 
509 	kbps = (bw%1000000)/1000;
510 	mbps = bw/1000000;
511 	if (kbps != 0) {
512 		if (mbps == 0)
513 			(void) snprintf(buf, DLADM_STRSIZE, "0.%03u", kbps);
514 		else
515 			(void) snprintf(buf, DLADM_STRSIZE, "%5u.%03u", mbps,
516 			    kbps);
517 	} else {
518 		(void) snprintf(buf, DLADM_STRSIZE, "%5u", mbps);
519 	}
520 
521 	return (buf);
522 }
523 
524 #define	LOCK_DB_PERMS	S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
525 
526 static int
527 i_dladm_lock_db(const char *lock_file, short type)
528 {
529 	int	lock_fd;
530 	struct	flock lock;
531 
532 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
533 	    LOCK_DB_PERMS)) < 0)
534 		return (-1);
535 
536 	lock.l_type = type;
537 	lock.l_whence = SEEK_SET;
538 	lock.l_start = 0;
539 	lock.l_len = 0;
540 
541 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
542 		int err = errno;
543 
544 		(void) close(lock_fd);
545 		(void) unlink(lock_file);
546 		errno = err;
547 		return (-1);
548 	}
549 	return (lock_fd);
550 }
551 
552 static void
553 i_dladm_unlock_db(const char *lock_file, int fd)
554 {
555 	struct flock lock;
556 
557 	if (fd < 0)
558 		return;
559 
560 	lock.l_type = F_UNLCK;
561 	lock.l_whence = SEEK_SET;
562 	lock.l_start = 0;
563 	lock.l_len = 0;
564 
565 	(void) fcntl(fd, F_SETLKW, &lock);
566 	(void) close(fd);
567 	(void) unlink(lock_file);
568 }
569 
570 /*
571  * Given a link class, returns its class string.
572  */
573 const char *
574 dladm_class2str(datalink_class_t class, char *buf)
575 {
576 	const char *s;
577 
578 	switch (class) {
579 	case DATALINK_CLASS_PHYS:
580 		s = "phys";
581 		break;
582 	case DATALINK_CLASS_VLAN:
583 		s = "vlan";
584 		break;
585 	case DATALINK_CLASS_AGGR:
586 		s = "aggr";
587 		break;
588 	case DATALINK_CLASS_VNIC:
589 		s = "vnic";
590 		break;
591 	case DATALINK_CLASS_ETHERSTUB:
592 		s = "etherstub";
593 		break;
594 	case DATALINK_CLASS_IPTUN:
595 		s = "iptun";
596 		break;
597 	case DATALINK_CLASS_SIMNET:
598 		s = "simnet";
599 		break;
600 	case DATALINK_CLASS_BRIDGE:
601 		s = "bridge";
602 		break;
603 	default:
604 		s = "unknown";
605 		break;
606 	}
607 
608 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
609 	return (buf);
610 }
611 
612 /*
613  * Given a physical link media type, returns its media type string.
614  */
615 const char *
616 dladm_media2str(uint32_t media, char *buf)
617 {
618 	const char *s = "--";
619 	media_type_t *mt;
620 	int idx;
621 
622 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
623 		mt = media_type_table + idx;
624 		if (mt->media_type == media) {
625 			s = mt->media_type_str;
626 			break;
627 		}
628 	}
629 
630 	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
631 	return (buf);
632 }
633 
634 /*
635  * Given a physical link media type string, returns its media type constant.
636  */
637 uint32_t
638 dladm_str2media(const char *buf)
639 {
640 	media_type_t *mt;
641 	int idx;
642 
643 	for (idx = 0; idx < MEDIATYPECOUNT; idx++) {
644 		mt = media_type_table + idx;
645 		if (strcasecmp(buf, mt->media_type_str) == 0)
646 			return (mt->media_type);
647 	}
648 
649 	return (DL_OTHER);
650 }
651 
652 dladm_status_t
653 i_dladm_rw_db(dladm_handle_t handle, const char *db_file, mode_t db_perms,
654     dladm_status_t (*process_db)(dladm_handle_t, void *, FILE *, FILE *),
655     void *arg, boolean_t writeop)
656 {
657 	dladm_status_t	status = DLADM_STATUS_OK;
658 	FILE		*fp, *nfp = NULL;
659 	char		lock[MAXPATHLEN];
660 	char		file[MAXPATHLEN];
661 	char		newfile[MAXPATHLEN];
662 	char		*db_basename;
663 	int		nfd, lock_fd;
664 
665 	/*
666 	 * If we are called from a boot script such as net-physical,
667 	 * it's quite likely that the root fs is still not writable.
668 	 * For this case, it's ok for the lock creation to fail since
669 	 * no one else could be accessing our configuration file.
670 	 */
671 	db_basename = strrchr(db_file, '/');
672 	if (db_basename == NULL || db_basename[1] == '\0')
673 		return (dladm_errno2status(EINVAL));
674 	db_basename++;
675 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
676 	if ((lock_fd = i_dladm_lock_db
677 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
678 		return (dladm_errno2status(errno));
679 
680 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
681 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
682 		int	err = errno;
683 
684 		i_dladm_unlock_db(lock, lock_fd);
685 		if (err == ENOENT)
686 			return (DLADM_STATUS_DBNOTFOUND);
687 
688 		return (dladm_errno2status(err));
689 	}
690 
691 	if (writeop) {
692 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
693 		    dladm_rootdir, db_file);
694 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
695 		    db_perms)) < 0) {
696 			(void) fclose(fp);
697 			i_dladm_unlock_db(lock, lock_fd);
698 			return (dladm_errno2status(errno));
699 		}
700 
701 		if ((nfp = fdopen(nfd, "w")) == NULL) {
702 			(void) close(nfd);
703 			(void) fclose(fp);
704 			(void) unlink(newfile);
705 			i_dladm_unlock_db(lock, lock_fd);
706 			return (dladm_errno2status(errno));
707 		}
708 	}
709 	status = (*process_db)(handle, arg, fp, nfp);
710 	if (!writeop || status != DLADM_STATUS_OK)
711 		goto done;
712 
713 	/*
714 	 * Configuration files need to be owned by the 'dladm' user.
715 	 * If we are invoked by root, the file ownership needs to be fixed.
716 	 */
717 	if (getuid() == 0 || geteuid() == 0) {
718 		if (fchown(nfd, UID_DLADM, GID_SYS) < 0) {
719 			status = dladm_errno2status(errno);
720 			goto done;
721 		}
722 	}
723 
724 	if (fflush(nfp) == EOF) {
725 		status = dladm_errno2status(errno);
726 		goto done;
727 	}
728 	(void) fclose(fp);
729 	(void) fclose(nfp);
730 
731 	if (rename(newfile, file) < 0) {
732 		(void) unlink(newfile);
733 		i_dladm_unlock_db(lock, lock_fd);
734 		return (dladm_errno2status(errno));
735 	}
736 
737 	i_dladm_unlock_db(lock, lock_fd);
738 	return (DLADM_STATUS_OK);
739 
740 done:
741 	if (nfp != NULL) {
742 		(void) fclose(nfp);
743 		if (status != DLADM_STATUS_OK)
744 			(void) unlink(newfile);
745 	}
746 	(void) fclose(fp);
747 	i_dladm_unlock_db(lock, lock_fd);
748 	return (status);
749 }
750 
751 dladm_status_t
752 dladm_set_rootdir(const char *rootdir)
753 {
754 	DIR	*dp;
755 
756 	if (rootdir == NULL || *rootdir != '/' ||
757 	    (dp = opendir(rootdir)) == NULL)
758 		return (DLADM_STATUS_BADARG);
759 
760 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
761 	(void) closedir(dp);
762 	return (DLADM_STATUS_OK);
763 }
764 
765 boolean_t
766 dladm_valid_linkname(const char *link)
767 {
768 	size_t		len = strlen(link);
769 	const char	*cp;
770 
771 	if (len >= MAXLINKNAMELEN)
772 		return (B_FALSE);
773 
774 	/*
775 	 * The link name cannot start with a digit and must end with a digit.
776 	 */
777 	if ((isdigit(link[0]) != 0) || (isdigit(link[len - 1]) == 0))
778 		return (B_FALSE);
779 
780 	/*
781 	 * The legal characters in a link name are:
782 	 * alphanumeric (a-z,  A-Z,  0-9), underscore ('_'), and '.'.
783 	 */
784 	for (cp = link; *cp != '\0'; cp++) {
785 		if ((isalnum(*cp) == 0) && (*cp != '_') && (*cp != '.'))
786 			return (B_FALSE);
787 	}
788 
789 	return (B_TRUE);
790 }
791 
792 /*
793  * Convert priority string to a value.
794  */
795 dladm_status_t
796 dladm_str2pri(char *token, mac_priority_level_t *pri)
797 {
798 	if (strlen(token) == strlen("low") &&
799 	    strncasecmp(token, "low", strlen("low")) == 0) {
800 		*pri = MPL_LOW;
801 	} else if (strlen(token) == strlen("medium") &&
802 	    strncasecmp(token, "medium", strlen("medium")) == 0) {
803 		*pri = MPL_MEDIUM;
804 	} else if (strlen(token) == strlen("high") &&
805 	    strncasecmp(token, "high", strlen("high")) == 0) {
806 		*pri = MPL_HIGH;
807 	} else {
808 		return (DLADM_STATUS_BADVAL);
809 	}
810 	return (DLADM_STATUS_OK);
811 }
812 
813 /*
814  * Convert priority value to a string.
815  */
816 const char *
817 dladm_pri2str(mac_priority_level_t pri, char *buf)
818 {
819 	const char	*s;
820 
821 	switch (pri) {
822 	case MPL_LOW:
823 		s = "low";
824 		break;
825 	case MPL_MEDIUM:
826 		s = "medium";
827 		break;
828 	case MPL_HIGH:
829 		s = "high";
830 		break;
831 	default:
832 		s = "--";
833 		break;
834 	}
835 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
836 	return (buf);
837 }
838 
839 void
840 dladm_free_args(dladm_arg_list_t *list)
841 {
842 	if (list != NULL) {
843 		free(list->al_buf);
844 		free(list);
845 	}
846 }
847 
848 dladm_status_t
849 dladm_parse_args(char *str, dladm_arg_list_t **listp, boolean_t novalues)
850 {
851 	dladm_arg_list_t	*list;
852 	dladm_arg_info_t	*aip;
853 	char			*buf, *curr;
854 	int			len, i;
855 
856 	if (str == NULL)
857 		return (DLADM_STATUS_BADVAL);
858 
859 	if (str[0] == '\0')
860 		return (DLADM_STATUS_OK);
861 
862 	list = malloc(sizeof (dladm_arg_list_t));
863 	if (list == NULL)
864 		return (dladm_errno2status(errno));
865 
866 	list->al_count = 0;
867 	list->al_buf = buf = strdup(str);
868 	if (buf == NULL)
869 		return (dladm_errno2status(errno));
870 
871 	curr = buf;
872 	len = strlen(buf);
873 	aip = NULL;
874 	for (i = 0; i < len; i++) {
875 		char		c = buf[i];
876 		boolean_t	match = (c == '=' || c == ',');
877 
878 		if (!match && i != len - 1)
879 			continue;
880 
881 		if (match) {
882 			buf[i] = '\0';
883 			if (*curr == '\0')
884 				goto fail;
885 		}
886 
887 		if (aip != NULL && c != '=') {
888 			if (aip->ai_count > DLADM_MAX_ARG_VALS)
889 				goto fail;
890 
891 			if (novalues)
892 				goto fail;
893 
894 			aip->ai_val[aip->ai_count] = curr;
895 			aip->ai_count++;
896 		} else {
897 			if (list->al_count > DLADM_MAX_ARG_VALS)
898 				goto fail;
899 
900 			aip = &list->al_info[list->al_count];
901 			aip->ai_name = curr;
902 			aip->ai_count = 0;
903 			list->al_count++;
904 			if (c == ',')
905 				aip = NULL;
906 		}
907 		curr = buf + i + 1;
908 	}
909 
910 	*listp = list;
911 	return (DLADM_STATUS_OK);
912 
913 fail:
914 	dladm_free_args(list);
915 	return (DLADM_STATUS_FAILED);
916 }
917