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