Print this page
Fixes to allow compilation on OmniOS and OI
OS-3342+co
OS-3007 dlmgmtd needs to work with non-native zones
OS-375 i_dls_mgmt_upcall()/dlmgmt_zfop() deadlock in dlmgmtd
OS-383 dladm rename-link doesn't update /etc/svc/volatile/dladm/network-datalink-management:default.cache
OS-249
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/dlmgmtd/dlmgmt_db.c
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_db.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
|
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 + * Copyright 2014, Joyent Inc. All rights reserved.
24 25 */
25 26
26 27 #include <assert.h>
27 28 #include <ctype.h>
28 29 #include <errno.h>
29 30 #include <fcntl.h>
30 31 #include <stdio.h>
31 32 #include <stdlib.h>
32 33 #include <string.h>
33 34 #include <strings.h>
34 35 #include <syslog.h>
35 36 #include <zone.h>
|
↓ open down ↓ |
2 lines elided |
↑ open up ↑ |
36 37 #include <sys/types.h>
37 38 #include <sys/stat.h>
38 39 #include <stropts.h>
39 40 #include <sys/conf.h>
40 41 #include <pthread.h>
41 42 #include <unistd.h>
42 43 #include <wait.h>
43 44 #include <libcontract.h>
44 45 #include <libcontract_priv.h>
45 46 #include <sys/contract/process.h>
47 +#include <sys/vnic.h>
48 +#include <zone.h>
46 49 #include "dlmgmt_impl.h"
47 50
48 51 typedef enum dlmgmt_db_op {
49 52 DLMGMT_DB_OP_WRITE,
50 53 DLMGMT_DB_OP_DELETE,
51 54 DLMGMT_DB_OP_READ
52 55 } dlmgmt_db_op_t;
53 56
54 57 typedef struct dlmgmt_db_req_s {
55 58 struct dlmgmt_db_req_s *ls_next;
56 59 dlmgmt_db_op_t ls_op;
57 60 char ls_link[MAXLINKNAMELEN];
58 61 datalink_id_t ls_linkid;
59 62 zoneid_t ls_zoneid;
60 63 uint32_t ls_flags; /* Either DLMGMT_ACTIVE or */
61 64 /* DLMGMT_PERSIST, not both. */
62 65 } dlmgmt_db_req_t;
63 66
64 67 /*
65 68 * List of pending db updates (e.g., because of a read-only filesystem).
66 69 */
67 70 static dlmgmt_db_req_t *dlmgmt_db_req_head = NULL;
68 71 static dlmgmt_db_req_t *dlmgmt_db_req_tail = NULL;
69 72
70 73 /*
71 74 * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
72 75 * line with an old format. This will cause the file being read to be
73 76 * re-written with the current format.
74 77 */
75 78 static boolean_t rewrite_needed;
76 79
77 80 static int dlmgmt_db_update(dlmgmt_db_op_t, const char *,
78 81 dlmgmt_link_t *, uint32_t);
79 82 static int dlmgmt_process_db_req(dlmgmt_db_req_t *);
80 83 static int dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
81 84 static void *dlmgmt_db_update_thread(void *);
82 85 static boolean_t process_link_line(char *, dlmgmt_link_t *);
83 86 static int process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
84 87 static int process_db_read(dlmgmt_db_req_t *, FILE *);
85 88 static void generate_link_line(dlmgmt_link_t *, boolean_t, char *);
86 89
87 90 #define BUFLEN(lim, ptr) (((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
88 91 #define MAXLINELEN 1024
89 92
90 93 typedef void db_walk_func_t(dlmgmt_link_t *);
91 94
92 95 /*
93 96 * Translator functions to go from dladm_datatype_t to character strings.
94 97 * Each function takes a pointer to a buffer, the size of the buffer,
95 98 * the name of the attribute, and the value to be written. The functions
96 99 * return the number of bytes written to the buffer. If the buffer is not big
97 100 * enough to hold the string representing the value, then nothing is written
98 101 * and 0 is returned.
99 102 */
100 103 typedef size_t write_func_t(char *, size_t, char *, void *);
101 104
102 105 /*
103 106 * Translator functions to read from a NULL terminated string buffer into
104 107 * something of the given DLADM_TYPE_*. The functions each return the number
105 108 * of bytes read from the string buffer. If there is an error reading data
106 109 * from the buffer, then 0 is returned. It is the caller's responsibility
107 110 * to free the data allocated by these functions.
108 111 */
109 112 typedef size_t read_func_t(char *, void **);
110 113
111 114 typedef struct translator_s {
112 115 const char *type_name;
113 116 write_func_t *write_func;
114 117 read_func_t *read_func;
115 118 } translator_t;
116 119
117 120 /*
118 121 * Translator functions, defined later but declared here so that
119 122 * the translator table can be defined.
120 123 */
121 124 static write_func_t write_str, write_boolean, write_uint64;
122 125 static read_func_t read_str, read_boolean, read_int64;
123 126
124 127 /*
125 128 * Translator table, indexed by dladm_datatype_t.
126 129 */
127 130 static translator_t translators[] = {
128 131 { "string", write_str, read_str },
129 132 { "boolean", write_boolean, read_boolean },
130 133 { "int", write_uint64, read_int64 }
131 134 };
132 135
133 136 static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
134 137
135 138 #define LINK_PROPERTY_DELIMINATOR ";"
136 139 #define LINK_PROPERTY_TYPE_VALUE_SEP ","
137 140 #define BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
138 141 strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
139 142 strlen(LINK_PROPERTY_DELIMINATOR) +\
140 143 strlen((n)))
141 144 #define GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
142 145 (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
143 146 translators[(type)].type_name, \
144 147 LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
145 148
146 149 /*
147 150 * Name of the cache file to keep the active <link name, linkid> mapping
148 151 */
149 152 char cachefile[MAXPATHLEN];
150 153
151 154 #define DLMGMT_PERSISTENT_DB_PATH "/etc/dladm/datalink.conf"
152 155 #define DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent) \
153 156 (void) snprintf((buffer), MAXPATHLEN, "%s", \
154 157 (persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
155 158
156 159 typedef struct zopen_arg {
157 160 const char *zopen_modestr;
158 161 int *zopen_pipe;
159 162 int zopen_fd;
160 163 } zopen_arg_t;
161 164
162 165 typedef struct zrename_arg {
163 166 const char *zrename_newname;
164 167 } zrename_arg_t;
165 168
166 169 typedef union zfoparg {
167 170 zopen_arg_t zfop_openarg;
168 171 zrename_arg_t zfop_renamearg;
169 172 } zfoparg_t;
170 173
171 174 typedef struct zfcbarg {
172 175 boolean_t zfarg_inglobalzone; /* is callback in global zone? */
173 176 zoneid_t zfarg_finglobalzone; /* is file in global zone? */
174 177 const char *zfarg_filename;
175 178 zfoparg_t *zfarg_oparg;
176 179 } zfarg_t;
177 180 #define zfarg_openarg zfarg_oparg->zfop_openarg
178 181 #define zfarg_renamearg zfarg_oparg->zfop_renamearg
179 182
180 183 /* zone file callback */
181 184 typedef int zfcb_t(zfarg_t *);
182 185
183 186 /*
184 187 * Execute an operation on filename relative to zoneid's zone root. If the
185 188 * file is in the global zone, then the zfcb() callback will simply be called
186 189 * directly. If the file is in a non-global zone, then zfcb() will be called
187 190 * both from the global zone's context, and from the non-global zone's context
188 191 * (from a fork()'ed child that has entered the non-global zone). This is
189 192 * done to allow the callback to communicate with itself if needed (e.g. to
190 193 * pass back the file descriptor of an opened file).
191 194 */
192 195 static int
193 196 dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
194 197 zfoparg_t *zfoparg)
195 198 {
196 199 int ctfd;
197 200 int err;
198 201 pid_t childpid;
199 202 siginfo_t info;
200 203 zfarg_t zfarg;
201 204 ctid_t ct;
202 205
203 206 if (zoneid != GLOBAL_ZONEID) {
204 207 /*
205 208 * We need to access a file that isn't in the global zone.
206 209 * Accessing non-global zone files from the global zone is
207 210 * unsafe (due to symlink attacks), we'll need to fork a child
208 211 * that enters the zone in question and executes the callback
209 212 * that will operate on the file.
210 213 *
211 214 * Before we proceed with this zone tango, we need to create a
212 215 * new process contract for the child, as required by
213 216 * zone_enter().
214 217 */
215 218 errno = 0;
216 219 ctfd = open64("/system/contract/process/template", O_RDWR);
217 220 if (ctfd == -1)
218 221 return (errno);
219 222 if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
220 223 (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
221 224 (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
222 225 (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
223 226 (err = ct_tmpl_activate(ctfd)) != 0) {
224 227 (void) close(ctfd);
225 228 return (err);
226 229 }
227 230 childpid = fork();
228 231 switch (childpid) {
229 232 case -1:
230 233 (void) ct_tmpl_clear(ctfd);
231 234 (void) close(ctfd);
232 235 return (err);
233 236 case 0:
234 237 (void) ct_tmpl_clear(ctfd);
235 238 (void) close(ctfd);
236 239 /*
237 240 * Elevate our privileges as zone_enter() requires all
238 241 * privileges.
239 242 */
240 243 if ((err = dlmgmt_elevate_privileges()) != 0)
241 244 _exit(err);
242 245 if (zone_enter(zoneid) == -1)
243 246 _exit(errno);
244 247 if ((err = dlmgmt_drop_privileges()) != 0)
245 248 _exit(err);
246 249 break;
247 250 default:
248 251 if (contract_latest(&ct) == -1)
249 252 ct = -1;
250 253 (void) ct_tmpl_clear(ctfd);
251 254 (void) close(ctfd);
252 255 if (waitid(P_PID, childpid, &info, WEXITED) == -1) {
253 256 (void) contract_abandon_id(ct);
254 257 return (errno);
255 258 }
256 259 (void) contract_abandon_id(ct);
257 260 if (info.si_status != 0)
258 261 return (info.si_status);
259 262 }
260 263 }
261 264
262 265 zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
263 266 zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
264 267 zfarg.zfarg_filename = filename;
265 268 zfarg.zfarg_oparg = zfoparg;
266 269 err = zfcb(&zfarg);
267 270 if (!zfarg.zfarg_inglobalzone)
268 271 _exit(err);
269 272 return (err);
270 273 }
271 274
272 275 static int
273 276 dlmgmt_zopen_cb(zfarg_t *zfarg)
274 277 {
275 278 struct strrecvfd recvfd;
276 279 boolean_t newfile = B_FALSE;
277 280 boolean_t inglobalzone = zfarg->zfarg_inglobalzone;
278 281 zoneid_t finglobalzone = zfarg->zfarg_finglobalzone;
279 282 const char *filename = zfarg->zfarg_filename;
280 283 const char *modestr = zfarg->zfarg_openarg.zopen_modestr;
281 284 int *p = zfarg->zfarg_openarg.zopen_pipe;
282 285 struct stat statbuf;
283 286 int oflags;
284 287 mode_t mode;
285 288 int fd = -1;
286 289 int err;
287 290
288 291 /* We only ever open a file for reading or writing, not both. */
289 292 oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
290 293 mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
291 294
292 295 /* Open the file if we're in the same zone as the file. */
293 296 if (inglobalzone == finglobalzone) {
294 297 /*
295 298 * First determine if we will be creating the file as part of
296 299 * opening it. If so, then we'll need to ensure that it has
297 300 * the proper ownership after having opened it.
298 301 */
299 302 if (oflags & O_CREAT) {
300 303 if (stat(filename, &statbuf) == -1) {
301 304 if (errno == ENOENT)
302 305 newfile = B_TRUE;
303 306 else
304 307 return (errno);
305 308 }
306 309 }
307 310 if ((fd = open(filename, oflags, mode)) == -1)
308 311 return (errno);
309 312 if (newfile) {
310 313 if (chown(filename, UID_DLADM, GID_NETADM) == -1) {
311 314 err = errno;
312 315 (void) close(fd);
313 316 return (err);
314 317 }
315 318 }
316 319 }
317 320
318 321 /*
319 322 * If we're not in the global zone, send the file-descriptor back to
320 323 * our parent in the global zone.
321 324 */
322 325 if (!inglobalzone) {
323 326 assert(!finglobalzone);
324 327 assert(fd != -1);
325 328 return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
326 329 }
327 330
328 331 /*
329 332 * At this point, we know we're in the global zone. If the file was
330 333 * in a non-global zone, receive the file-descriptor from our child in
331 334 * the non-global zone.
332 335 */
333 336 if (!finglobalzone) {
334 337 if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
335 338 return (errno);
336 339 fd = recvfd.fd;
337 340 }
338 341
339 342 zfarg->zfarg_openarg.zopen_fd = fd;
340 343 return (0);
341 344 }
342 345
343 346 static int
344 347 dlmgmt_zunlink_cb(zfarg_t *zfarg)
345 348 {
346 349 if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
347 350 return (0);
348 351 return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
349 352 }
350 353
351 354 static int
352 355 dlmgmt_zrename_cb(zfarg_t *zfarg)
353 356 {
354 357 if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
355 358 return (0);
356 359 return (rename(zfarg->zfarg_filename,
357 360 zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
358 361 }
359 362
360 363 /*
361 364 * Same as fopen(3C), except that it opens the file relative to zoneid's zone
362 365 * root.
363 366 */
364 367 static FILE *
365 368 dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
366 369 int *err)
367 370 {
368 371 int p[2];
369 372 zfoparg_t zfoparg;
370 373 FILE *fp = NULL;
371 374
372 375 if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
373 376 *err = errno;
374 377 return (NULL);
375 378 }
376 379
377 380 zfoparg.zfop_openarg.zopen_modestr = modestr;
378 381 zfoparg.zfop_openarg.zopen_pipe = p;
379 382 *err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
380 383 if (zoneid != GLOBAL_ZONEID) {
381 384 (void) close(p[0]);
382 385 (void) close(p[1]);
383 386 }
384 387 if (*err == 0) {
385 388 fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
386 389 if (fp == NULL) {
387 390 *err = errno;
388 391 (void) close(zfoparg.zfop_openarg.zopen_fd);
389 392 }
390 393 }
391 394 return (fp);
392 395 }
393 396
394 397 /*
395 398 * Same as rename(2), except that old and new are relative to zoneid's zone
396 399 * root.
397 400 */
398 401 static int
399 402 dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
400 403 {
401 404 zfoparg_t zfoparg;
402 405
403 406 zfoparg.zfop_renamearg.zrename_newname = new;
404 407 return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
405 408 }
406 409
407 410 /*
408 411 * Same as unlink(2), except that filename is relative to zoneid's zone root.
409 412 */
410 413 static int
411 414 dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
412 415 {
413 416 return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
414 417 }
415 418
416 419 static size_t
417 420 write_str(char *buffer, size_t buffer_length, char *name, void *value)
418 421 {
419 422 char *ptr = value;
420 423 size_t data_length = strnlen(ptr, buffer_length);
421 424
422 425 /*
423 426 * Strings are assumed to be NULL terminated. In order to fit in
424 427 * the buffer, the string's length must be less then buffer_length.
425 428 * If the value is empty, there's no point in writing it, in fact,
426 429 * we shouldn't even see that case.
427 430 */
428 431 if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
429 432 buffer_length || data_length == 0)
430 433 return (0);
431 434
432 435 /*
433 436 * Since we know the string will fit in the buffer, snprintf will
434 437 * always return less than buffer_length, so we can just return
435 438 * whatever snprintf returns.
436 439 */
437 440 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
438 441 name, DLADM_TYPE_STR, ptr));
439 442 }
440 443
441 444 static size_t
442 445 write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
443 446 {
444 447 boolean_t *ptr = value;
445 448
446 449 /*
447 450 * Booleans are either zero or one, so we only need room for two
448 451 * characters in the buffer.
449 452 */
450 453 if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
451 454 return (0);
452 455
453 456 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
454 457 name, DLADM_TYPE_BOOLEAN, *ptr));
455 458 }
456 459
457 460 static size_t
458 461 write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
459 462 {
460 463 uint64_t *ptr = value;
461 464
462 465 /*
463 466 * Limit checking for uint64_t is a little trickier.
464 467 */
465 468 if (snprintf(NULL, 0, "%lld", *ptr) +
466 469 BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
467 470 return (0);
468 471
469 472 return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
470 473 name, DLADM_TYPE_UINT64, *ptr));
471 474 }
472 475
473 476 static size_t
474 477 read_str(char *buffer, void **value)
475 478 {
476 479 char *ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
477 480 ssize_t len;
478 481
479 482 if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
480 483 >= MAXLINKATTRVALLEN) {
481 484 free(ptr);
482 485 return (0);
483 486 }
484 487
485 488 *(char **)value = ptr;
486 489
487 490 /* Account for NULL terminator */
488 491 return (len + 1);
489 492 }
490 493
491 494 static size_t
492 495 read_boolean(char *buffer, void **value)
493 496 {
494 497 boolean_t *ptr = calloc(1, sizeof (boolean_t));
495 498
496 499 if (ptr == NULL)
497 500 return (0);
498 501
499 502 *ptr = atoi(buffer);
500 503 *(boolean_t **)value = ptr;
501 504
502 505 return (sizeof (boolean_t));
503 506 }
504 507
505 508 static size_t
506 509 read_int64(char *buffer, void **value)
507 510 {
508 511 int64_t *ptr = calloc(1, sizeof (int64_t));
509 512
510 513 if (ptr == NULL)
511 514 return (0);
512 515
513 516 *ptr = (int64_t)atoll(buffer);
514 517 *(int64_t **)value = ptr;
515 518
516 519 return (sizeof (int64_t));
517 520 }
518 521
519 522 static dlmgmt_db_req_t *
520 523 dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
521 524 datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
522 525 {
523 526 dlmgmt_db_req_t *req;
524 527
525 528 if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
526 529 *err = errno;
527 530 } else {
528 531 req->ls_op = op;
529 532 if (linkname != NULL)
530 533 (void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
531 534 req->ls_linkid = linkid;
532 535 req->ls_zoneid = zoneid;
533 536 req->ls_flags = flags;
534 537 }
535 538 return (req);
536 539 }
537 540
538 541 /*
539 542 * Update the db entry with name "entryname" using information from "linkp".
540 543 */
541 544 static int
542 545 dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
543 546 uint32_t flags)
544 547 {
|
↓ open down ↓ |
489 lines elided |
↑ open up ↑ |
545 548 dlmgmt_db_req_t *req;
546 549 int err;
547 550
548 551 /* It is either a persistent request or an active request, not both. */
549 552 assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
550 553
551 554 if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
552 555 linkp->ll_zoneid, flags, &err)) == NULL)
553 556 return (err);
554 557
558 + /* If transient op and onloan, use the global zone cache file. */
559 + if (flags == DLMGMT_ACTIVE && linkp->ll_onloan)
560 + req->ls_zoneid = GLOBAL_ZONEID;
561 +
555 562 /*
556 563 * If the return error is EINPROGRESS, this request is handled
557 564 * asynchronously; return success.
558 565 */
559 566 err = dlmgmt_process_db_req(req);
560 567 if (err != EINPROGRESS)
561 568 free(req);
562 569 else
563 570 err = 0;
564 571 return (err);
565 572 }
566 573
567 574 #define DLMGMT_DB_OP_STR(op) \
568 575 (((op) == DLMGMT_DB_OP_READ) ? "read" : \
569 576 (((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
570 577
571 578 #define DLMGMT_DB_CONF_STR(flag) \
572 579 (((flag) == DLMGMT_ACTIVE) ? "active" : \
573 580 (((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
574 581
575 582 static int
576 583 dlmgmt_process_db_req(dlmgmt_db_req_t *req)
577 584 {
578 585 pthread_t tid;
579 586 boolean_t writeop;
580 587 int err;
581 588
582 589 /*
583 590 * If there are already pending "write" requests, queue this request in
584 591 * the pending list. Note that this function is called while the
585 592 * dlmgmt_rw_lock is held, so it is safe to access the global variables.
586 593 */
587 594 writeop = (req->ls_op != DLMGMT_DB_OP_READ);
588 595 if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
589 596 (dlmgmt_db_req_head != NULL)) {
590 597 dlmgmt_db_req_tail->ls_next = req;
591 598 dlmgmt_db_req_tail = req;
592 599 return (EINPROGRESS);
593 600 }
594 601
595 602 err = dlmgmt_process_db_onereq(req, writeop);
596 603 if (err != EINPROGRESS && err != 0 && err != ENOENT) {
597 604 /*
598 605 * Log the error unless the request processing is still in
599 606 * progress or if the configuration file hasn't been created
600 607 * yet (ENOENT).
601 608 */
602 609 dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
603 610 "operation on %s configuration failed: %s",
604 611 DLMGMT_DB_OP_STR(req->ls_op),
605 612 DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
606 613 }
607 614
608 615 if (err == EINPROGRESS) {
609 616 assert(req->ls_flags == DLMGMT_PERSIST);
610 617 assert(writeop && dlmgmt_db_req_head == NULL);
611 618 dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
612 619 err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
613 620 if (err == 0)
614 621 return (EINPROGRESS);
615 622 }
616 623 return (err);
617 624 }
618 625
619 626 static int
620 627 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
621 628 {
622 629 int err = 0;
623 630 FILE *fp, *nfp = NULL;
624 631 char file[MAXPATHLEN];
625 632 char newfile[MAXPATHLEN];
626 633
627 634 DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
628 635 fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
629 636 /*
630 637 * Note that it is not an error if the file doesn't exist. If we're
631 638 * reading, we treat this case the same way as an empty file. If
632 639 * we're writing, the file will be created when we open the file for
633 640 * writing below.
634 641 */
635 642 if (fp == NULL && !writeop)
636 643 return (err);
637 644
638 645 if (writeop) {
639 646 (void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
640 647 nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
641 648 if (nfp == NULL) {
642 649 /*
643 650 * EROFS can happen at boot when the file system is
644 651 * read-only. Return EINPROGRESS so that the caller
645 652 * can add this request to the pending request list
646 653 * and start a retry thread.
647 654 */
648 655 err = (errno == EROFS ? EINPROGRESS : errno);
649 656 goto done;
650 657 }
651 658 }
652 659 if (writeop) {
653 660 if ((err = process_db_write(req, fp, nfp)) == 0)
654 661 err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
655 662 } else {
656 663 err = process_db_read(req, fp);
657 664 }
658 665
659 666 done:
660 667 if (nfp != NULL) {
661 668 (void) fclose(nfp);
662 669 if (err != 0)
663 670 (void) dlmgmt_zunlink(newfile, req->ls_zoneid);
664 671 }
665 672 (void) fclose(fp);
666 673 return (err);
667 674 }
668 675
669 676 /*ARGSUSED*/
670 677 static void *
671 678 dlmgmt_db_update_thread(void *arg)
672 679 {
673 680 dlmgmt_db_req_t *req;
674 681
675 682 dlmgmt_table_lock(B_TRUE);
676 683
677 684 assert(dlmgmt_db_req_head != NULL);
678 685 while ((req = dlmgmt_db_req_head) != NULL) {
679 686 assert(req->ls_flags == DLMGMT_PERSIST);
680 687 if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
681 688 /*
682 689 * The filesystem is still read only. Go to sleep and
683 690 * try again.
684 691 */
685 692 dlmgmt_table_unlock();
686 693 (void) sleep(5);
687 694 dlmgmt_table_lock(B_TRUE);
688 695 continue;
689 696 }
690 697
691 698 /*
692 699 * The filesystem is no longer read only. Continue processing
693 700 * and remove the request from the pending list.
694 701 */
695 702 dlmgmt_db_req_head = req->ls_next;
696 703 if (dlmgmt_db_req_tail == req) {
697 704 assert(dlmgmt_db_req_head == NULL);
698 705 dlmgmt_db_req_tail = NULL;
699 706 }
700 707 free(req);
701 708 }
702 709
703 710 dlmgmt_table_unlock();
704 711 return (NULL);
705 712 }
706 713
|
↓ open down ↓ |
142 lines elided |
↑ open up ↑ |
707 714 static int
708 715 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
709 716 {
710 717 boolean_t found_type = B_FALSE;
711 718 dladm_datatype_t type = DLADM_TYPE_STR;
712 719 int i, len;
713 720 char *curr;
714 721 char attr_name[MAXLINKATTRLEN];
715 722 size_t attr_buf_len = 0;
716 723 void *attr_buf = NULL;
724 + boolean_t rename;
717 725
718 726 curr = buf;
719 727 len = strlen(buf);
720 728 attr_name[0] = '\0';
721 729 for (i = 0; i < len; i++) {
722 730 char c = buf[i];
723 731 boolean_t match = (c == '=' ||
724 732 (c == ',' && !found_type) || c == ';');
725 733
734 + rename = B_FALSE;
735 +
726 736 /*
727 737 * Move to the next character if there is no match and
728 738 * if we have not reached the last character.
729 739 */
730 740 if (!match && i != len - 1)
731 741 continue;
732 742
733 743 if (match) {
734 744 /*
735 745 * NUL-terminate the string pointed to by 'curr'.
736 746 */
737 747 buf[i] = '\0';
738 748 if (*curr == '\0')
739 749 goto parse_fail;
740 750 }
741 751
742 752 if (attr_name[0] != '\0' && found_type) {
743 753 /*
744 754 * We get here after we have processed the "<prop>="
745 755 * pattern. The pattern we are now interested in is
746 756 * "<val>;".
747 757 */
748 758 if (c == '=')
749 759 goto parse_fail;
750 760
751 761 if (strcmp(attr_name, "linkid") == 0) {
752 762 if (read_int64(curr, &attr_buf) == 0)
753 763 goto parse_fail;
754 764 linkp->ll_linkid =
755 765 (datalink_class_t)*(int64_t *)attr_buf;
756 766 } else if (strcmp(attr_name, "name") == 0) {
757 767 if (read_str(curr, &attr_buf) == 0)
758 768 goto parse_fail;
759 769 (void) snprintf(linkp->ll_link,
760 770 MAXLINKNAMELEN, "%s", attr_buf);
|
↓ open down ↓ |
25 lines elided |
↑ open up ↑ |
761 771 } else if (strcmp(attr_name, "class") == 0) {
762 772 if (read_int64(curr, &attr_buf) == 0)
763 773 goto parse_fail;
764 774 linkp->ll_class =
765 775 (datalink_class_t)*(int64_t *)attr_buf;
766 776 } else if (strcmp(attr_name, "media") == 0) {
767 777 if (read_int64(curr, &attr_buf) == 0)
768 778 goto parse_fail;
769 779 linkp->ll_media =
770 780 (uint32_t)*(int64_t *)attr_buf;
781 + } else if (strcmp(attr_name, "zone") == 0) {
782 + if (read_str(curr, &attr_buf) == 0)
783 + goto parse_fail;
784 + linkp->ll_zoneid = getzoneidbyname(attr_buf);
785 + if (linkp->ll_zoneid == -1) {
786 + if (errno == EFAULT)
787 + abort();
788 + /*
789 + * If we can't find the zone, assign the
790 + * link to the GZ and mark it for being
791 + * renamed.
792 + */
793 + linkp->ll_zoneid = 0;
794 + rename = B_TRUE;
795 + }
771 796 } else {
772 797 attr_buf_len = translators[type].read_func(curr,
773 798 &attr_buf);
774 799 if (attr_buf_len == 0)
775 800 goto parse_fail;
776 801
777 802 if (linkattr_set(&(linkp->ll_head), attr_name,
778 803 attr_buf, attr_buf_len, type) != 0) {
779 804 free(attr_buf);
780 805 goto parse_fail;
781 806 }
782 807 }
783 808
784 809 free(attr_buf);
785 810 attr_name[0] = '\0';
786 811 found_type = B_FALSE;
787 812 } else if (attr_name[0] != '\0') {
788 813 /*
789 814 * Non-zero length attr_name and found_type of false
790 815 * indicates that we have not found the type for this
791 816 * attribute. The pattern now is "<type>,<val>;", we
792 817 * want the <type> part of the pattern.
793 818 */
794 819 for (type = 0; type < ntranslators; type++) {
795 820 if (strcmp(curr,
796 821 translators[type].type_name) == 0) {
797 822 found_type = B_TRUE;
798 823 break;
799 824 }
800 825 }
801 826
802 827 if (!found_type)
803 828 goto parse_fail;
|
↓ open down ↓ |
23 lines elided |
↑ open up ↑ |
804 829 } else {
805 830 /*
806 831 * A zero length attr_name indicates we are looking
807 832 * at the beginning of a link attribute.
808 833 */
809 834 if (c != '=')
810 835 goto parse_fail;
811 836
812 837 (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
813 838 }
839 +
840 + /*
841 + * The zone that this link belongs to has died, we are
842 + * reparenting it to the GZ and renaming it to avoid name
843 + * collisions.
844 + */
845 + if (rename == B_TRUE) {
846 + (void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
847 + "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
848 + }
814 849 curr = buf + i + 1;
815 850 }
816 851
817 852 /* Correct any erroneous IPTUN datalink class constant in the file */
818 853 if (linkp->ll_class == 0x60) {
819 854 linkp->ll_class = DATALINK_CLASS_IPTUN;
820 855 rewrite_needed = B_TRUE;
821 856 }
822 857
823 858 return (0);
824 859
825 860 parse_fail:
826 861 /*
827 862 * Free linkp->ll_head (link attribute list)
828 863 */
829 864 linkattr_destroy(linkp);
830 865 return (-1);
831 866 }
832 867
833 868 static boolean_t
834 869 process_link_line(char *buf, dlmgmt_link_t *linkp)
835 870 {
836 871 int i, len, llen;
837 872 char *str, *lasts;
838 873 char tmpbuf[MAXLINELEN];
839 874
840 875 bzero(linkp, sizeof (*linkp));
841 876 linkp->ll_linkid = DATALINK_INVALID_LINKID;
842 877
843 878 /*
844 879 * Use a copy of buf for parsing so that we can do whatever we want.
845 880 */
846 881 (void) strlcpy(tmpbuf, buf, MAXLINELEN);
847 882
848 883 /*
849 884 * Skip leading spaces, blank lines, and comments.
850 885 */
851 886 len = strlen(tmpbuf);
852 887 for (i = 0; i < len; i++) {
853 888 if (!isspace(tmpbuf[i]))
854 889 break;
855 890 }
856 891 if (i == len || tmpbuf[i] == '#')
857 892 return (B_TRUE);
858 893
859 894 str = tmpbuf + i;
860 895 /*
861 896 * Find the link name and assign it to the link structure.
862 897 */
863 898 if (strtok_r(str, " \n\t", &lasts) == NULL)
864 899 goto fail;
865 900
866 901 llen = strlen(str);
867 902 /*
868 903 * Note that a previous version of the persistent datalink.conf file
869 904 * stored the linkid as the first field. In that case, the name will
870 905 * be obtained through parse_linkprops from a property with the format
871 906 * "name=<linkname>". If we encounter such a format, we set
872 907 * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
873 908 * the new format after it's done reading in the data.
874 909 */
875 910 if (isdigit(str[0])) {
876 911 linkp->ll_linkid = atoi(str);
877 912 rewrite_needed = B_TRUE;
878 913 } else {
879 914 if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
880 915 sizeof (linkp->ll_link))
881 916 goto fail;
882 917 }
883 918
884 919 str += llen + 1;
885 920 if (str >= tmpbuf + len)
886 921 goto fail;
887 922
888 923 /*
889 924 * Now find the list of link properties.
890 925 */
891 926 if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
892 927 goto fail;
893 928
894 929 if (parse_linkprops(str, linkp) < 0)
895 930 goto fail;
896 931
897 932 return (B_TRUE);
898 933
899 934 fail:
900 935 /*
901 936 * Delete corrupted line.
902 937 */
903 938 buf[0] = '\0';
904 939 return (B_FALSE);
905 940 }
906 941
907 942 /*
908 943 * Find any properties in linkp that refer to "old", and rename to "new".
909 944 * Return B_TRUE if any renaming occurred.
910 945 */
911 946 static int
912 947 dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
913 948 boolean_t *renamed)
914 949 {
915 950 dlmgmt_linkattr_t *attrp;
916 951 char *newval = NULL, *pname;
917 952 char valcp[MAXLINKATTRVALLEN];
918 953 size_t newsize;
919 954
920 955 *renamed = B_FALSE;
921 956
922 957 if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
923 958 (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
924 959 if (strcmp(old, (char *)attrp->lp_val) == 0) {
925 960 newsize = strlen(new) + 1;
926 961 if ((newval = malloc(newsize)) == NULL)
927 962 return (errno);
928 963 (void) strcpy(newval, new);
929 964 free(attrp->lp_val);
930 965 attrp->lp_val = newval;
931 966 attrp->lp_sz = newsize;
932 967 *renamed = B_TRUE;
933 968 }
934 969 return (0);
935 970 }
936 971
937 972 if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
938 973 return (0);
939 974
940 975 /* <linkname>:[<linkname>:]... */
941 976 if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
942 977 return (errno);
943 978
944 979 bcopy(attrp->lp_val, valcp, sizeof (valcp));
945 980 pname = strtok(valcp, ":");
946 981 while (pname != NULL) {
947 982 if (strcmp(pname, old) == 0) {
948 983 (void) strcat(newval, new);
949 984 *renamed = B_TRUE;
950 985 } else {
951 986 (void) strcat(newval, pname);
952 987 }
953 988 (void) strcat(newval, ":");
954 989 pname = strtok(NULL, ":");
955 990 }
956 991 if (*renamed) {
957 992 free(attrp->lp_val);
958 993 attrp->lp_val = newval;
959 994 attrp->lp_sz = strlen(newval) + 1;
960 995 } else {
961 996 free(newval);
962 997 }
963 998 return (0);
964 999 }
965 1000
966 1001 static int
967 1002 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
968 1003 {
969 1004 boolean_t done = B_FALSE;
970 1005 int err = 0;
971 1006 dlmgmt_link_t link_in_file, *linkp = NULL, *dblinkp;
972 1007 boolean_t persist = (req->ls_flags == DLMGMT_PERSIST);
973 1008 boolean_t writeall, rename, attr_renamed;
974 1009 char buf[MAXLINELEN];
975 1010
976 1011 writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
977 1012
978 1013 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
979 1014 /*
980 1015 * find the link in the avl tree with the given linkid.
981 1016 */
982 1017 linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
983 1018 if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
984 1019 /*
985 1020 * This link has already been changed. This could
986 1021 * happen if the request is pending because of
987 1022 * read-only file-system. If so, we are done.
988 1023 */
989 1024 return (0);
990 1025 }
991 1026 /*
992 1027 * In the case of a rename, linkp's name has been updated to
993 1028 * the new name, and req->ls_link is the old link name.
994 1029 */
995 1030 rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
996 1031 }
997 1032
998 1033 /*
999 1034 * fp can be NULL if the file didn't initially exist and we're
1000 1035 * creating it as part of this write operation.
1001 1036 */
1002 1037 if (fp == NULL)
1003 1038 goto write;
1004 1039
1005 1040 while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
1006 1041 process_link_line(buf, &link_in_file)) {
1007 1042 /*
1008 1043 * Only the link name is needed. Free the memory allocated for
1009 1044 * the link attributes list of link_in_file.
1010 1045 */
1011 1046 linkattr_destroy(&link_in_file);
1012 1047
1013 1048 if (link_in_file.ll_link[0] == '\0' || done) {
1014 1049 /*
1015 1050 * this is a comment line or we are done updating the
1016 1051 * line for the specified link, write the rest of
1017 1052 * lines out.
1018 1053 */
1019 1054 if (fputs(buf, nfp) == EOF)
1020 1055 err = errno;
1021 1056 continue;
1022 1057 }
1023 1058
1024 1059 switch (req->ls_op) {
1025 1060 case DLMGMT_DB_OP_WRITE:
1026 1061 /*
1027 1062 * For write operations, we generate a new output line
1028 1063 * if we're either writing all links (writeall) or if
1029 1064 * the name of the link in the file matches the one
1030 1065 * we're looking for. Otherwise, we write out the
1031 1066 * buffer as-is.
1032 1067 *
1033 1068 * If we're doing a rename operation, ensure that any
1034 1069 * references to the link being renamed in link
1035 1070 * properties are also updated before we write
1036 1071 * anything.
1037 1072 */
1038 1073 if (writeall) {
1039 1074 linkp = link_by_name(link_in_file.ll_link,
1040 1075 req->ls_zoneid);
1041 1076 }
1042 1077 if (writeall || strcmp(req->ls_link,
1043 1078 link_in_file.ll_link) == 0) {
1044 1079 generate_link_line(linkp, persist, buf);
1045 1080 if (!writeall && !rename)
1046 1081 done = B_TRUE;
1047 1082 } else if (rename && persist) {
1048 1083 dblinkp = link_by_name(link_in_file.ll_link,
1049 1084 req->ls_zoneid);
1050 1085 err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1051 1086 linkp->ll_link, &attr_renamed);
1052 1087 if (err != 0)
1053 1088 break;
1054 1089 if (attr_renamed) {
1055 1090 generate_link_line(dblinkp, persist,
1056 1091 buf);
1057 1092 }
1058 1093 }
1059 1094 if (fputs(buf, nfp) == EOF)
1060 1095 err = errno;
1061 1096 break;
1062 1097 case DLMGMT_DB_OP_DELETE:
1063 1098 /*
1064 1099 * Delete is simple. If buf does not represent the
1065 1100 * link we're deleting, write it out.
1066 1101 */
1067 1102 if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1068 1103 if (fputs(buf, nfp) == EOF)
1069 1104 err = errno;
1070 1105 } else {
1071 1106 done = B_TRUE;
1072 1107 }
1073 1108 break;
1074 1109 case DLMGMT_DB_OP_READ:
1075 1110 default:
1076 1111 err = EINVAL;
1077 1112 break;
1078 1113 }
1079 1114 }
1080 1115
1081 1116 write:
1082 1117 /*
1083 1118 * If we get to the end of the file and have not seen what linkid
1084 1119 * points to, write it out then.
1085 1120 */
1086 1121 if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1087 1122 generate_link_line(linkp, persist, buf);
1088 1123 done = B_TRUE;
1089 1124 if (fputs(buf, nfp) == EOF)
1090 1125 err = errno;
1091 1126 }
1092 1127
1093 1128 return (err);
1094 1129 }
1095 1130
1096 1131 static int
1097 1132 process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1098 1133 {
1099 1134 avl_index_t name_where, id_where;
1100 1135 dlmgmt_link_t link_in_file, *newlink, *link_in_db;
1101 1136 char buf[MAXLINELEN];
1102 1137 int err = 0;
1103 1138
1104 1139 /*
1105 1140 * This loop processes each line of the configuration file.
1106 1141 */
1107 1142 while (fgets(buf, MAXLINELEN, fp) != NULL) {
1108 1143 if (!process_link_line(buf, &link_in_file)) {
1109 1144 err = EINVAL;
1110 1145 break;
1111 1146 }
1112 1147
1113 1148 /*
1114 1149 * Skip the comment line.
1115 1150 */
1116 1151 if (link_in_file.ll_link[0] == '\0') {
1117 1152 linkattr_destroy(&link_in_file);
1118 1153 continue;
1119 1154 }
1120 1155
1121 1156 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1122 1157 link_in_file.ll_linkid == DATALINK_INVALID_LINKID) {
1123 1158 linkattr_destroy(&link_in_file);
1124 1159 continue;
1125 1160 }
1126 1161
1127 1162 link_in_file.ll_zoneid = req->ls_zoneid;
1128 1163 link_in_db = link_by_name(link_in_file.ll_link,
1129 1164 link_in_file.ll_zoneid);
1130 1165 if (link_in_db != NULL) {
1131 1166 /*
1132 1167 * If the link in the database already has the flag
1133 1168 * for this request set, then the entry is a
1134 1169 * duplicate. If it's not a duplicate, then simply
1135 1170 * turn on the appropriate flag on the existing link.
1136 1171 */
1137 1172 if (link_in_db->ll_flags & req->ls_flags) {
1138 1173 dlmgmt_log(LOG_WARNING, "Duplicate links "
1139 1174 "in the repository: %s",
1140 1175 link_in_file.ll_link);
1141 1176 linkattr_destroy(&link_in_file);
1142 1177 } else {
1143 1178 if (req->ls_flags & DLMGMT_PERSIST) {
1144 1179 /*
1145 1180 * Save the newly read properties into
1146 1181 * the existing link.
1147 1182 */
1148 1183 assert(link_in_db->ll_head == NULL);
1149 1184 link_in_db->ll_head =
1150 1185 link_in_file.ll_head;
1151 1186 } else {
1152 1187 linkattr_destroy(&link_in_file);
1153 1188 }
1154 1189 link_in_db->ll_flags |= req->ls_flags;
1155 1190 }
1156 1191 } else {
1157 1192 /*
1158 1193 * This is a new link. Allocate a new dlmgmt_link_t
1159 1194 * and add it to the trees.
1160 1195 */
1161 1196 newlink = calloc(1, sizeof (*newlink));
1162 1197 if (newlink == NULL) {
1163 1198 dlmgmt_log(LOG_WARNING, "Unable to allocate "
1164 1199 "memory to create new link %s",
1165 1200 link_in_file.ll_link);
1166 1201 linkattr_destroy(&link_in_file);
1167 1202 continue;
1168 1203 }
1169 1204 bcopy(&link_in_file, newlink, sizeof (*newlink));
1170 1205
1171 1206 if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1172 1207 newlink->ll_linkid = dlmgmt_nextlinkid;
1173 1208 if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1174 1209 NULL) {
1175 1210 dlmgmt_log(LOG_WARNING, "Link ID %d is already"
1176 1211 " in use, destroying link %s",
1177 1212 newlink->ll_linkid, newlink->ll_link);
1178 1213 link_destroy(newlink);
1179 1214 continue;
1180 1215 }
1181 1216
1182 1217 if ((req->ls_flags & DLMGMT_ACTIVE) &&
1183 1218 link_activate(newlink) != 0) {
1184 1219 dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1185 1220 newlink->ll_link);
1186 1221 link_destroy(newlink);
1187 1222 continue;
1188 1223 }
1189 1224
1190 1225 avl_insert(&dlmgmt_id_avl, newlink, id_where);
1191 1226 /*
1192 1227 * link_activate call above can insert newlink in
1193 1228 * dlmgmt_name_avl tree when activating a link that is
1194 1229 * assigned to a NGZ.
1195 1230 */
1196 1231 if (avl_find(&dlmgmt_name_avl, newlink,
1197 1232 &name_where) == NULL)
1198 1233 avl_insert(&dlmgmt_name_avl, newlink,
1199 1234 name_where);
1200 1235
1201 1236 dlmgmt_advance(newlink);
1202 1237 newlink->ll_flags |= req->ls_flags;
1203 1238 }
1204 1239 }
1205 1240
1206 1241 return (err);
1207 1242 }
1208 1243
1209 1244 /*
1210 1245 * Generate an entry in the link database.
1211 1246 * Each entry has this format:
1212 1247 * <link name> <prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1213 1248 */
1214 1249 static void
|
↓ open down ↓ |
391 lines elided |
↑ open up ↑ |
1215 1250 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1216 1251 {
1217 1252 char tmpbuf[MAXLINELEN];
1218 1253 char *ptr = tmpbuf;
1219 1254 char *lim = tmpbuf + MAXLINELEN;
1220 1255 dlmgmt_linkattr_t *cur_p = NULL;
1221 1256 uint64_t u64;
1222 1257
1223 1258 ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1224 1259 if (!persist) {
1260 + char zname[ZONENAME_MAX];
1225 1261 /*
1226 - * We store the linkid in the active database so that dlmgmtd
1227 - * can recover in the event that it is restarted.
1262 + * We store the linkid and the zone name in the active database
1263 + * so that dlmgmtd can recover in the event that it is
1264 + * restarted.
1228 1265 */
1229 1266 u64 = linkp->ll_linkid;
1230 1267 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1268 +
1269 + if (getzonenamebyid(linkp->ll_zoneid, zname,
1270 + sizeof (zname)) != -1) {
1271 + ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
1272 + }
1231 1273 }
1232 1274 u64 = linkp->ll_class;
1233 1275 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1234 1276 u64 = linkp->ll_media;
1235 1277 ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1236 1278
1237 1279 /*
1238 1280 * The daemon does not keep any active link attribute. Only store the
1239 1281 * attributes if this request is for persistent configuration,
1240 1282 */
1241 1283 if (persist) {
1242 1284 for (cur_p = linkp->ll_head; cur_p != NULL;
1243 1285 cur_p = cur_p->lp_next) {
1244 1286 ptr += translators[cur_p->lp_type].write_func(ptr,
1245 1287 BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1246 1288 }
1247 1289 }
1248 1290
1249 1291 if (ptr <= lim)
1250 1292 (void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1251 1293 }
1252 1294
1253 1295 int
1254 1296 dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1255 1297 {
1256 1298 return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1257 1299 flags));
1258 1300 }
1259 1301
1260 1302 int
1261 1303 dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1262 1304 uint32_t flags)
1263 1305 {
1264 1306 int err;
1265 1307
1266 1308 if (flags & DLMGMT_PERSIST) {
1267 1309 if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1268 1310 linkp, DLMGMT_PERSIST)) != 0) {
1269 1311 return (err);
1270 1312 }
1271 1313 }
1272 1314
1273 1315 if (flags & DLMGMT_ACTIVE) {
1274 1316 if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1275 1317 linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1276 1318 (void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1277 1319 linkp, DLMGMT_PERSIST);
1278 1320 return (err);
1279 1321 }
1280 1322 }
1281 1323
1282 1324 return (0);
1283 1325 }
1284 1326
1285 1327 /*
1286 1328 * Upgrade properties that have link IDs as values to link names. Because '.'
1287 1329 * is a valid linkname character, the port separater for link aggregations
1288 1330 * must be changed to ':'.
1289 1331 */
1290 1332 static void
1291 1333 linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1292 1334 {
1293 1335 datalink_id_t linkid;
1294 1336 char *portidstr;
1295 1337 char portname[MAXLINKNAMELEN + 1];
1296 1338 dlmgmt_link_t *linkp;
1297 1339 char *new_attr_val;
1298 1340 size_t new_attr_sz;
1299 1341 boolean_t upgraded = B_FALSE;
1300 1342
1301 1343 if (strcmp(attrp->lp_name, "linkover") == 0 ||
1302 1344 strcmp(attrp->lp_name, "simnetpeer") == 0) {
1303 1345 if (attrp->lp_type == DLADM_TYPE_UINT64) {
1304 1346 linkid = (datalink_id_t)*(uint64_t *)attrp->lp_val;
1305 1347 if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1306 1348 return;
1307 1349 new_attr_sz = strlen(linkp->ll_link) + 1;
1308 1350 if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1309 1351 return;
1310 1352 (void) strcpy(new_attr_val, linkp->ll_link);
1311 1353 upgraded = B_TRUE;
1312 1354 }
1313 1355 } else if (strcmp(attrp->lp_name, "portnames") == 0) {
1314 1356 /*
1315 1357 * The old format for "portnames" was
1316 1358 * "<linkid>.[<linkid>.]...". The new format is
1317 1359 * "<linkname>:[<linkname>:]...".
1318 1360 */
1319 1361 if (!isdigit(((char *)attrp->lp_val)[0]))
1320 1362 return;
1321 1363 new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1322 1364 if (new_attr_val == NULL)
1323 1365 return;
1324 1366 portidstr = (char *)attrp->lp_val;
1325 1367 while (*portidstr != '\0') {
1326 1368 errno = 0;
1327 1369 linkid = strtol(portidstr, &portidstr, 10);
1328 1370 if (linkid == 0 || *portidstr != '.' ||
1329 1371 (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1330 1372 NULL) {
1331 1373 free(new_attr_val);
1332 1374 return;
1333 1375 }
1334 1376 (void) snprintf(portname, sizeof (portname), "%s:",
1335 1377 linkp->ll_link);
1336 1378 if (strlcat(new_attr_val, portname,
1337 1379 MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1338 1380 free(new_attr_val);
1339 1381 return;
1340 1382 }
1341 1383 /* skip the '.' delimiter */
1342 1384 portidstr++;
1343 1385 }
1344 1386 new_attr_sz = strlen(new_attr_val) + 1;
1345 1387 upgraded = B_TRUE;
1346 1388 }
1347 1389
1348 1390 if (upgraded) {
1349 1391 attrp->lp_type = DLADM_TYPE_STR;
1350 1392 attrp->lp_sz = new_attr_sz;
1351 1393 free(attrp->lp_val);
1352 1394 attrp->lp_val = new_attr_val;
1353 1395 }
1354 1396 }
1355 1397
1356 1398 static void
1357 1399 dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1358 1400 {
1359 1401 dlmgmt_linkattr_t *attrp;
1360 1402
1361 1403 for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1362 1404 linkattr_upgrade(attrp);
1363 1405 }
1364 1406
1365 1407 static void
1366 1408 dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1367 1409 {
1368 1410 linkp->ll_flags |= DLMGMT_ACTIVE;
1369 1411 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1370 1412 }
1371 1413
1372 1414 static void
1373 1415 dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1374 1416 {
|
↓ open down ↓ |
134 lines elided |
↑ open up ↑ |
1375 1417 dlmgmt_link_t *linkp;
1376 1418
1377 1419 for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1378 1420 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1379 1421 if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1380 1422 func(linkp);
1381 1423 }
1382 1424 }
1383 1425
1384 1426 /*
1427 + * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
1428 + *
1429 + * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
1430 + * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
1431 + * Because of the upcall architecture of dlmgmtd this can lead to deadlock
1432 + * with the following scenario:
1433 + * a) the thread preparing to fork will have acquired the malloc locks
1434 + * then attempt to suspend every thread in preparation to fork.
1435 + * b) all of the upcalls will be blocked in door_ucred() trying to malloc()
1436 + * and get the credentials of their caller.
1437 + * c) we can't suspend the in-kernel thread making the upcall.
1438 + *
1439 + * Thus, we cannot serve door requests because we're blocked in malloc()
1440 + * which fork() owns, but fork() is in turn blocked on the in-kernel thread
1441 + * making the door upcall. This is a fundamental architectural problem with
1442 + * any server handling upcalls and also trying to fork().
1443 + *
1444 + * To minimize the chance of this deadlock occuring, we check ahead of time to
1445 + * see if the file we want to read actually exists in the zone (which it almost
1446 + * never does), so we don't need fork in that case (i.e. rarely to never).
1447 + */
1448 +static boolean_t
1449 +zone_file_exists(char *zoneroot, char *filename)
1450 +{
1451 + struct stat sb;
1452 + char fname[MAXPATHLEN];
1453 +
1454 + (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
1455 +
1456 + if (stat(fname, &sb) == -1)
1457 + return (B_FALSE);
1458 +
1459 + return (B_TRUE);
1460 +}
1461 +
1462 +/*
1385 1463 * Initialize the datalink <link name, linkid> mapping and the link's
1386 1464 * attributes list based on the configuration file /etc/dladm/datalink.conf
1387 1465 * and the active configuration cache file
1388 1466 * /etc/svc/volatile/dladm/datalink-management:default.cache.
1389 1467 */
1390 1468 int
1391 -dlmgmt_db_init(zoneid_t zoneid)
1469 +dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
1392 1470 {
1393 1471 dlmgmt_db_req_t *req;
1394 1472 int err;
1395 1473 boolean_t boot = B_FALSE;
1396 1474
1397 1475 if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1398 1476 DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1399 1477 return (err);
1400 1478
1401 - if ((err = dlmgmt_process_db_req(req)) != 0) {
1402 - /*
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.
1407 - */
1408 - if (err != ENOENT)
1409 - goto done;
1479 + /* Handle running in a non-native branded zone (i.e. has /native) */
1480 + if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) {
1481 + char tdir[MAXPATHLEN];
1482 +
1483 + (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
1484 + (void) strlcpy(cachefile, tdir, sizeof (cachefile));
1485 + }
1486 +
1487 + if (zone_file_exists(zoneroot, cachefile)) {
1488 + if ((err = dlmgmt_process_db_req(req)) != 0) {
1489 + /*
1490 + * If we get back ENOENT, that means that the active
1491 + * configuration file doesn't exist yet, and is not an
1492 + * error. We'll create it down below after we've
1493 + * loaded the persistent configuration.
1494 + */
1495 + if (err != ENOENT)
1496 + goto done;
1497 + boot = B_TRUE;
1498 + }
1499 + } else {
1410 1500 boot = B_TRUE;
1411 1501 }
1412 1502
1413 - req->ls_flags = DLMGMT_PERSIST;
1414 - err = dlmgmt_process_db_req(req);
1415 - if (err != 0 && err != ENOENT)
1416 - goto done;
1503 + if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) {
1504 + req->ls_flags = DLMGMT_PERSIST;
1505 + err = dlmgmt_process_db_req(req);
1506 + if (err != 0 && err != ENOENT)
1507 + goto done;
1508 + }
1417 1509 err = 0;
1418 1510 if (rewrite_needed) {
1419 1511 /*
1420 1512 * First update links in memory, then dump the entire db to
1421 1513 * disk.
1422 1514 */
1423 1515 dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1424 1516 req->ls_op = DLMGMT_DB_OP_WRITE;
1425 1517 req->ls_linkid = DATALINK_ALL_LINKID;
1426 1518 if ((err = dlmgmt_process_db_req(req)) != 0 &&
1427 1519 err != EINPROGRESS)
1428 1520 goto done;
1429 1521 }
1430 1522 if (boot) {
1431 1523 dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1432 1524 dlmgmt_db_phys_activate);
1433 1525 }
1434 1526
|
↓ open down ↓ |
8 lines elided |
↑ open up ↑ |
1435 1527 done:
1436 1528 if (err == EINPROGRESS)
1437 1529 err = 0;
1438 1530 else
1439 1531 free(req);
1440 1532 return (err);
1441 1533 }
1442 1534
1443 1535 /*
1444 1536 * Remove all links in the given zoneid.
1537 + *
1538 + * We do this work in two different passes. In the first pass, we remove any
1539 + * entry that hasn't been loaned and mark every entry that has been loaned as
1540 + * something that is going to be tombstomed. In the second pass, we drop the
1541 + * table lock for every entry and remove the tombstombed entry for our zone.
1445 1542 */
1446 1543 void
1447 1544 dlmgmt_db_fini(zoneid_t zoneid)
1448 1545 {
1449 1546 dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1450 1547
1451 1548 while (linkp != NULL) {
1452 1549 next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1453 1550 if (linkp->ll_zoneid == zoneid) {
1454 - (void) dlmgmt_destroy_common(linkp,
1455 - DLMGMT_ACTIVE | DLMGMT_PERSIST);
1551 + boolean_t onloan = linkp->ll_onloan;
1552 +
1553 + /*
1554 + * Cleanup any VNICs that were loaned to the zone
1555 + * before the zone goes away and we can no longer
1556 + * refer to the VNIC by the name/zoneid.
1557 + */
1558 + if (onloan) {
1559 + (void) dlmgmt_delete_db_entry(linkp,
1560 + DLMGMT_ACTIVE);
1561 + linkp->ll_tomb = B_TRUE;
1562 + } else {
1563 + (void) dlmgmt_destroy_common(linkp,
1564 + DLMGMT_ACTIVE | DLMGMT_PERSIST);
1565 + }
1566 +
1456 1567 }
1457 1568 linkp = next_linkp;
1458 1569 }
1570 +
1571 +again:
1572 + linkp = avl_first(&dlmgmt_name_avl);
1573 + while (linkp != NULL) {
1574 + vnic_ioc_delete_t ioc;
1575 +
1576 + next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1577 +
1578 + if (linkp->ll_zoneid != zoneid) {
1579 + linkp = next_linkp;
1580 + continue;
1581 + }
1582 + ioc.vd_vnic_id = linkp->ll_linkid;
1583 + if (linkp->ll_tomb != B_TRUE)
1584 + abort();
1585 +
1586 + /*
1587 + * We have to drop the table lock while going up into the
1588 + * kernel. If we hold the table lock while deleting a vnic, we
1589 + * may get blocked on the mac perimeter and the holder of it may
1590 + * want something from dlmgmtd.
1591 + */
1592 + dlmgmt_table_unlock();
1593 +
1594 + if (ioctl(dladm_dld_fd(dld_handle),
1595 + VNIC_IOC_DELETE, &ioc) < 0)
1596 + dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini "
1597 + "delete VNIC ioctl failed %d %d",
1598 + ioc.vd_vnic_id, errno);
1599 +
1600 + /*
1601 + * Even though we've dropped the lock, we know that nothing else
1602 + * could have removed us. Therefore, it should be safe to go
1603 + * through and delete ourselves, but do nothing else. We'll have
1604 + * to restart iteration from the beginning. This can be painful.
1605 + */
1606 + dlmgmt_table_lock(B_TRUE);
1607 +
1608 + (void) dlmgmt_destroy_common(linkp,
1609 + DLMGMT_ACTIVE | DLMGMT_PERSIST);
1610 + goto again;
1611 + }
1612 +
1459 1613 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX