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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <unistd.h>
29 #include <stropts.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <strings.h>
33 #include <dirent.h>
34 #include <sys/stat.h>
35 #include <libdladm.h>
36 #include <libdladm_impl.h>
37 #include <libintl.h>
38 
39 static char		dladm_rootdir[MAXPATHLEN] = "/";
40 
41 /*
42  * Issue an ioctl to the specified file descriptor attached to the
43  * DLD control driver interface.
44  */
45 int
46 i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len)
47 {
48 	struct strioctl	iocb;
49 
50 	iocb.ic_cmd = ic_cmd;
51 	iocb.ic_timout = 0;
52 	iocb.ic_len = ic_len;
53 	iocb.ic_dp = (char *)ic_dp;
54 
55 	return (ioctl(fd, I_STR, &iocb));
56 }
57 
58 const char *
59 dladm_status2str(dladm_status_t status, char *buf)
60 {
61 	const char	*s;
62 
63 	switch (status) {
64 	case DLADM_STATUS_OK:
65 		s = "ok";
66 		break;
67 	case DLADM_STATUS_BADARG:
68 		s = "invalid argument";
69 		break;
70 	case DLADM_STATUS_FAILED:
71 		s = "operation failed";
72 		break;
73 	case DLADM_STATUS_TOOSMALL:
74 		s = "buffer size too small";
75 		break;
76 	case DLADM_STATUS_NOTSUP:
77 		s = "operation not supported";
78 		break;
79 	case DLADM_STATUS_NOTFOUND:
80 		s = "object not found";
81 		break;
82 	case DLADM_STATUS_BADVAL:
83 		s = "invalid value";
84 		break;
85 	case DLADM_STATUS_NOMEM:
86 		s = "insufficient memory";
87 		break;
88 	case DLADM_STATUS_EXIST:
89 		s = "object already exists";
90 		break;
91 	case DLADM_STATUS_LINKINVAL:
92 		s = "invalid link";
93 		break;
94 	case DLADM_STATUS_PROPRDONLY:
95 		s = "read-only property";
96 		break;
97 	case DLADM_STATUS_BADVALCNT:
98 		s = "invalid number of values";
99 		break;
100 	case DLADM_STATUS_DBNOTFOUND:
101 		s = "database not found";
102 		break;
103 	case DLADM_STATUS_DENIED:
104 		s = "permission denied";
105 		break;
106 	case DLADM_STATUS_IOERR:
107 		s = "I/O error";
108 		break;
109 	case DLADM_STATUS_TEMPONLY:
110 		s = "change cannot be persistent, specify -t please";
111 		break;
112 	case DLADM_STATUS_TIMEDOUT:
113 		s = "operation timed out";
114 		break;
115 	case DLADM_STATUS_ISCONN:
116 		s = "already connected";
117 		break;
118 	case DLADM_STATUS_NOTCONN:
119 		s = "not connected";
120 		break;
121 	case DLADM_STATUS_REPOSITORYINVAL:
122 		s = "invalid configuration repository";
123 		break;
124 	case DLADM_STATUS_MACADDRINVAL:
125 		s = "invalid MAC address";
126 		break;
127 	case DLADM_STATUS_KEYINVAL:
128 		s = "invalid key";
129 		break;
130 	case DLADM_STATUS_INVALIDID:
131 		s = "invalid VNIC id";
132 		break;
133 	case DLADM_STATUS_INVALIDMACADDRLEN:
134 		s = "invalid MAC address length";
135 		break;
136 	case DLADM_STATUS_INVALIDMACADDRTYPE:
137 		s = "invalid MAC address type";
138 		break;
139 	case DLADM_STATUS_AUTOIDNOTEMP:
140 		s = "automatic VNIC ID assigment not supported with"
141 		    "persistant operations";
142 		break;
143 	case DLADM_STATUS_AUTOIDNOAVAILABLEID:
144 		s = "no available VNIC ID for automatic assignment";
145 		break;
146 	case DLADM_STATUS_BUSY:
147 		s = "device busy";
148 		break;
149 	default:
150 		s = "<unknown error>";
151 		break;
152 	}
153 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
154 	return (buf);
155 }
156 
157 /*
158  * Convert a unix errno to a dladm_status_t.
159  * We only convert errnos that are likely to be encountered. All others
160  * are mapped to DLADM_STATUS_FAILED.
161  */
162 dladm_status_t
163 dladm_errno2status(int err)
164 {
165 	switch (err) {
166 	case EINVAL:
167 		return (DLADM_STATUS_BADARG);
168 	case EEXIST:
169 		return (DLADM_STATUS_EXIST);
170 	case ENOENT:
171 		return (DLADM_STATUS_NOTFOUND);
172 	case ENOSPC:
173 		return (DLADM_STATUS_TOOSMALL);
174 	case ENOMEM:
175 		return (DLADM_STATUS_NOMEM);
176 	case ENOTSUP:
177 		return (DLADM_STATUS_NOTSUP);
178 	case EACCES:
179 		return (DLADM_STATUS_DENIED);
180 	case EIO:
181 		return (DLADM_STATUS_IOERR);
182 	case EBUSY:
183 		return (DLADM_STATUS_BUSY);
184 	default:
185 		return (DLADM_STATUS_FAILED);
186 	}
187 }
188 
189 /*
190  * These are the uid and gid of the user 'dladm'.
191  * The directory /etc/dladm and all files under it are owned by this user.
192  */
193 #define	DLADM_DB_OWNER		15
194 #define	DLADM_DB_GROUP		3
195 #define	LOCK_DB_PERMS		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
196 
197 static int
198 i_dladm_lock_db(const char *lock_file, short type)
199 {
200 	int	lock_fd;
201 	struct  flock lock;
202 
203 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
204 	    LOCK_DB_PERMS)) < 0)
205 		return (-1);
206 
207 	lock.l_type = type;
208 	lock.l_whence = SEEK_SET;
209 	lock.l_start = 0;
210 	lock.l_len = 0;
211 
212 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
213 		int err = errno;
214 
215 		(void) close(lock_fd);
216 		(void) unlink(lock_file);
217 		errno = err;
218 		return (-1);
219 	}
220 	return (lock_fd);
221 }
222 
223 static void
224 i_dladm_unlock_db(const char *lock_file, int fd)
225 {
226 	struct flock lock;
227 
228 	if (fd < 0)
229 		return;
230 
231 	lock.l_type = F_UNLCK;
232 	lock.l_whence = SEEK_SET;
233 	lock.l_start = 0;
234 	lock.l_len = 0;
235 
236 	(void) fcntl(fd, F_SETLKW, &lock);
237 	(void) close(fd);
238 	(void) unlink(lock_file);
239 }
240 
241 dladm_status_t
242 i_dladm_rw_db(const char *db_file, mode_t db_perms,
243     dladm_status_t (*process_db)(void *, FILE *, FILE *),
244     void *arg, boolean_t writeop)
245 {
246 	dladm_status_t	status = DLADM_STATUS_OK;
247 	FILE		*fp, *nfp = NULL;
248 	char		lock[MAXPATHLEN];
249 	char		file[MAXPATHLEN];
250 	char		newfile[MAXPATHLEN];
251 	char		*db_basename;
252 	int		nfd, lock_fd;
253 
254 	/*
255 	 * If we are called from a boot script such as net-physical,
256 	 * it's quite likely that the root fs is still not writable.
257 	 * For this case, it's ok for the lock creation to fail since
258 	 * no one else could be accessing our configuration file.
259 	 */
260 	db_basename = strrchr(db_file, '/');
261 	if (db_basename == NULL || db_basename[1] == '\0')
262 		return (dladm_errno2status(EINVAL));
263 	db_basename++;
264 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
265 	if ((lock_fd = i_dladm_lock_db
266 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
267 		return (dladm_errno2status(errno));
268 
269 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
270 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
271 		int	err = errno;
272 
273 		i_dladm_unlock_db(lock, lock_fd);
274 		if (err == ENOENT)
275 			return (DLADM_STATUS_DBNOTFOUND);
276 
277 		return (dladm_errno2status(err));
278 	}
279 
280 	if (writeop) {
281 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
282 		    dladm_rootdir, db_file);
283 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
284 		    db_perms)) < 0) {
285 			(void) fclose(fp);
286 			i_dladm_unlock_db(lock, lock_fd);
287 			return (dladm_errno2status(errno));
288 		}
289 
290 		if ((nfp = fdopen(nfd, "w")) == NULL) {
291 			(void) close(nfd);
292 			(void) fclose(fp);
293 			(void) unlink(newfile);
294 			i_dladm_unlock_db(lock, lock_fd);
295 			return (dladm_errno2status(errno));
296 		}
297 	}
298 	status = (*process_db)(arg, fp, nfp);
299 	if (!writeop || status != DLADM_STATUS_OK)
300 		goto done;
301 
302 	/*
303 	 * Configuration files need to be owned by the 'dladm' user.
304 	 * If we are invoked by root, the file ownership needs to be fixed.
305 	 */
306 	if (getuid() == 0 || geteuid() == 0) {
307 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
308 			status = dladm_errno2status(errno);
309 			goto done;
310 		}
311 	}
312 
313 	if (fflush(nfp) == EOF) {
314 		status = dladm_errno2status(errno);
315 		goto done;
316 	}
317 	(void) fclose(fp);
318 	(void) fclose(nfp);
319 
320 	if (rename(newfile, file) < 0) {
321 		(void) unlink(newfile);
322 		i_dladm_unlock_db(lock, lock_fd);
323 		return (dladm_errno2status(errno));
324 	}
325 
326 	i_dladm_unlock_db(lock, lock_fd);
327 	return (DLADM_STATUS_OK);
328 
329 done:
330 	if (nfp != NULL) {
331 		(void) fclose(nfp);
332 		if (status != DLADM_STATUS_OK)
333 			(void) unlink(newfile);
334 	}
335 	(void) fclose(fp);
336 	i_dladm_unlock_db(lock, lock_fd);
337 	return (status);
338 }
339 
340 dladm_status_t
341 dladm_set_rootdir(const char *rootdir)
342 {
343 	DIR	*dp;
344 
345 	if (rootdir == NULL || *rootdir != '/' ||
346 	    (dp = opendir(rootdir)) == NULL)
347 		return (DLADM_STATUS_BADARG);
348 
349 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
350 	(void) closedir(dp);
351 	return (DLADM_STATUS_OK);
352 }
353