1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 /* 26 * Copyright 2020 Joyent, Inc. 27 */ 28 29 #include <alloca.h> 30 #include <ctype.h> 31 #include <limits.h> 32 #include <syslog.h> 33 #include <strings.h> 34 #include <unistd.h> 35 #include <sys/fm/protocol.h> 36 #include <sys/systeminfo.h> 37 #include <sys/utsname.h> 38 39 #include <topo_error.h> 40 #include <topo_digraph.h> 41 #include <topo_subr.h> 42 43 void 44 topo_hdl_lock(topo_hdl_t *thp) 45 { 46 (void) pthread_mutex_lock(&thp->th_lock); 47 } 48 49 void 50 topo_hdl_unlock(topo_hdl_t *thp) 51 { 52 (void) pthread_mutex_unlock(&thp->th_lock); 53 } 54 55 const char * 56 topo_stability2name(topo_stability_t s) 57 { 58 switch (s) { 59 case TOPO_STABILITY_INTERNAL: return (TOPO_STABSTR_INTERNAL); 60 case TOPO_STABILITY_PRIVATE: return (TOPO_STABSTR_PRIVATE); 61 case TOPO_STABILITY_OBSOLETE: return (TOPO_STABSTR_OBSOLETE); 62 case TOPO_STABILITY_EXTERNAL: return (TOPO_STABSTR_EXTERNAL); 63 case TOPO_STABILITY_UNSTABLE: return (TOPO_STABSTR_UNSTABLE); 64 case TOPO_STABILITY_EVOLVING: return (TOPO_STABSTR_EVOLVING); 65 case TOPO_STABILITY_STABLE: return (TOPO_STABSTR_STABLE); 66 case TOPO_STABILITY_STANDARD: return (TOPO_STABSTR_STANDARD); 67 default: return (TOPO_STABSTR_UNKNOWN); 68 } 69 } 70 71 topo_stability_t 72 topo_name2stability(const char *name) 73 { 74 if (strcmp(name, TOPO_STABSTR_INTERNAL) == 0) 75 return (TOPO_STABILITY_INTERNAL); 76 else if (strcmp(name, TOPO_STABSTR_PRIVATE) == 0) 77 return (TOPO_STABILITY_PRIVATE); 78 else if (strcmp(name, TOPO_STABSTR_OBSOLETE) == 0) 79 return (TOPO_STABILITY_OBSOLETE); 80 else if (strcmp(name, TOPO_STABSTR_EXTERNAL) == 0) 81 return (TOPO_STABILITY_EXTERNAL); 82 else if (strcmp(name, TOPO_STABSTR_UNSTABLE) == 0) 83 return (TOPO_STABILITY_UNSTABLE); 84 else if (strcmp(name, TOPO_STABSTR_EVOLVING) == 0) 85 return (TOPO_STABILITY_EVOLVING); 86 else if (strcmp(name, TOPO_STABSTR_STABLE) == 0) 87 return (TOPO_STABILITY_STABLE); 88 else if (strcmp(name, TOPO_STABSTR_STANDARD) == 0) 89 return (TOPO_STABILITY_STANDARD); 90 91 return (TOPO_STABILITY_UNKNOWN); 92 } 93 94 static const topo_debug_mode_t _topo_dbout_modes[] = { 95 { "stderr", "send debug messages to stderr", TOPO_DBOUT_STDERR }, 96 { "syslog", "send debug messages to syslog", TOPO_DBOUT_SYSLOG }, 97 { NULL, NULL, 0 } 98 }; 99 100 static const topo_debug_mode_t _topo_dbflag_modes[] = { 101 { "error", "error handling debug messages enabled", TOPO_DBG_ERR }, 102 { "module", "module debug messages enabled", TOPO_DBG_MOD }, 103 { "modulesvc", "module services debug messages enabled", 104 TOPO_DBG_MODSVC }, 105 { "walk", "walker subsystem debug messages enabled", TOPO_DBG_WALK }, 106 { "xml", "xml file parsing messages enabled", TOPO_DBG_XML }, 107 { "devinfoforce", "devinfo DINFOFORCE snapshot used", TOPO_DBG_FORCE }, 108 { "all", "all debug modes enabled", TOPO_DBG_ALL}, 109 { NULL, NULL, 0 } 110 }; 111 112 void 113 env_process_value(topo_hdl_t *thp, const char *begin, const char *end) 114 { 115 char buf[MAXNAMELEN]; 116 size_t count; 117 topo_debug_mode_t *dbp; 118 119 while (begin < end && isspace(*begin)) 120 begin++; 121 122 while (begin < end && isspace(*(end - 1))) 123 end--; 124 125 if (begin >= end) 126 return; 127 128 count = end - begin; 129 count += 1; 130 131 if (count > sizeof (buf)) 132 return; 133 134 (void) snprintf(buf, count, "%s", begin); 135 136 for (dbp = (topo_debug_mode_t *)_topo_dbflag_modes; 137 dbp->tdm_name != NULL; ++dbp) { 138 if (strcmp(buf, dbp->tdm_name) == 0) 139 thp->th_debug |= dbp->tdm_mode; 140 } 141 } 142 143 void 144 topo_debug_set(topo_hdl_t *thp, const char *dbmode, const char *dout) 145 { 146 char *end, *value, *next; 147 topo_debug_mode_t *dbp; 148 149 topo_hdl_lock(thp); 150 value = (char *)dbmode; 151 152 for (end = (char *)dbmode; *end != '\0'; value = next) { 153 end = strchr(value, ','); 154 if (end != NULL) 155 next = end + 1; /* skip the comma */ 156 else 157 next = end = value + strlen(value); 158 159 env_process_value(thp, value, end); 160 } 161 162 if (dout == NULL) { 163 topo_hdl_unlock(thp); 164 return; 165 } 166 167 for (dbp = (topo_debug_mode_t *)_topo_dbout_modes; 168 dbp->tdm_name != NULL; ++dbp) { 169 if (strcmp(dout, dbp->tdm_name) == 0) 170 thp->th_dbout = dbp->tdm_mode; 171 } 172 topo_hdl_unlock(thp); 173 } 174 175 void 176 topo_vdprintf(topo_hdl_t *thp, const char *mod, const char *format, va_list ap) 177 { 178 char *msg; 179 size_t len; 180 char c; 181 182 len = vsnprintf(&c, 1, format, ap); 183 msg = alloca(len + 2); 184 (void) vsnprintf(msg, len + 1, format, ap); 185 186 if (msg[len - 1] != '\n') 187 (void) strcpy(&msg[len], "\n"); 188 189 if (thp->th_dbout == TOPO_DBOUT_SYSLOG) { 190 if (mod == NULL) { 191 syslog(LOG_DEBUG | LOG_USER, "libtopo DEBUG: %s", msg); 192 } else { 193 syslog(LOG_DEBUG | LOG_USER, "libtopo DEBUG: %s: %s", 194 mod, msg); 195 } 196 } else { 197 if (mod == NULL) { 198 (void) fprintf(stderr, "libtopo DEBUG: %s", msg); 199 } else { 200 (void) fprintf(stderr, "libtopo DEBUG: %s: %s", mod, 201 msg); 202 } 203 } 204 } 205 206 /*PRINTFLIKE3*/ 207 void 208 topo_dprintf(topo_hdl_t *thp, int mask, const char *format, ...) 209 { 210 va_list ap; 211 212 if (!(thp->th_debug & mask)) 213 return; 214 215 va_start(ap, format); 216 topo_vdprintf(thp, NULL, format, ap); 217 va_end(ap); 218 } 219 220 tnode_t * 221 topo_hdl_root(topo_hdl_t *thp, const char *scheme) 222 { 223 ttree_t *tp; 224 topo_digraph_t *tdg; 225 226 for (tp = topo_list_next(&thp->th_trees); tp != NULL; 227 tp = topo_list_next(tp)) { 228 if (strcmp(scheme, tp->tt_scheme) == 0) 229 return (tp->tt_root); 230 } 231 for (tdg = topo_list_next(&thp->th_digraphs); tdg != NULL; 232 tdg = topo_list_next(tdg)) { 233 if (strcmp(scheme, tdg->tdg_scheme) == 0) 234 return (tdg->tdg_rootnode); 235 } 236 237 return (NULL); 238 } 239 240 /* 241 * buf_append -- Append str to buf (if it's non-NULL). Place prepend 242 * in buf in front of str and append behind it (if they're non-NULL). 243 * Continue to update size even if we run out of space to actually 244 * stuff characters in the buffer. 245 */ 246 void 247 topo_fmristr_build(ssize_t *sz, char *buf, size_t buflen, char *str, 248 char *prepend, char *append) 249 { 250 ssize_t left; 251 252 if (str == NULL) 253 return; 254 255 if (buflen == 0 || (left = buflen - *sz) < 0) 256 left = 0; 257 258 if (buf != NULL && left != 0) 259 buf += *sz; 260 261 if (prepend == NULL && append == NULL) 262 *sz += snprintf(buf, left, "%s", str); 263 else if (append == NULL) 264 *sz += snprintf(buf, left, "%s%s", prepend, str); 265 else if (prepend == NULL) 266 *sz += snprintf(buf, left, "%s%s", str, append); 267 else 268 *sz += snprintf(buf, left, "%s%s%s", prepend, str, append); 269 } 270 271 #define TOPO_PLATFORM_PATH "%s/usr/platform/%s/lib/fm/topo/%s" 272 #define TOPO_COMMON_PATH "%s/usr/lib/fm/topo/%s" 273 274 char * 275 topo_search_path(topo_mod_t *mod, const char *rootdir, const char *file) 276 { 277 char *pp, sp[PATH_MAX]; 278 topo_hdl_t *thp = mod->tm_hdl; 279 280 /* 281 * Search for file name in order of platform, machine and common 282 * topo directories 283 */ 284 (void) snprintf(sp, PATH_MAX, TOPO_PLATFORM_PATH, rootdir, 285 thp->th_platform, file); 286 if (access(sp, F_OK) != 0) { 287 (void) snprintf(sp, PATH_MAX, TOPO_PLATFORM_PATH, 288 thp->th_rootdir, thp->th_machine, file); 289 if (access(sp, F_OK) != 0) { 290 (void) snprintf(sp, PATH_MAX, TOPO_COMMON_PATH, 291 thp->th_rootdir, file); 292 if (access(sp, F_OK) != 0) { 293 return (NULL); 294 } 295 } 296 } 297 298 pp = topo_mod_strdup(mod, sp); 299 300 return (pp); 301 } 302 303 /* 304 * SMBIOS serial numbers can contain characters (particularly ':' and ' ') 305 * that are invalid for the authority and can break FMRI parsing. We translate 306 * any invalid characters to a safe '-', as well as trimming any leading or 307 * trailing whitespace. Similarly, '/' can be found in some product names 308 * so we translate that to '-'. 309 */ 310 char * 311 topo_cleanup_auth_str(topo_hdl_t *thp, const char *begin) 312 { 313 char buf[MAXNAMELEN]; 314 const char *end, *cp; 315 char *pp; 316 char c; 317 int i; 318 319 end = begin + strlen(begin); 320 321 while (begin < end && isspace(*begin)) 322 begin++; 323 while (begin < end && isspace(*(end - 1))) 324 end--; 325 326 if (begin >= end) 327 return (NULL); 328 329 cp = begin; 330 for (i = 0; i < MAXNAMELEN - 1; i++) { 331 if (cp >= end) 332 break; 333 c = *cp; 334 if (c == ':' || c == '=' || c == '/' || isspace(c) || 335 !isprint(c)) 336 buf[i] = '-'; 337 else 338 buf[i] = c; 339 cp++; 340 } 341 buf[i] = 0; 342 343 pp = topo_hdl_strdup(thp, buf); 344 return (pp); 345 } 346 347 void 348 topo_sensor_type_name(uint32_t type, char *buf, size_t len) 349 { 350 topo_name_trans_t *ntp; 351 352 for (ntp = &topo_sensor_type_table[0]; ntp->int_name != NULL; ntp++) { 353 if (ntp->int_value == type) { 354 (void) strlcpy(buf, ntp->int_name, len); 355 return; 356 } 357 } 358 359 (void) snprintf(buf, len, "0x%02x", type); 360 } 361 362 void 363 topo_sensor_units_name(uint8_t type, char *buf, size_t len) 364 { 365 topo_name_trans_t *ntp; 366 367 for (ntp = &topo_units_type_table[0]; ntp->int_name != NULL; ntp++) { 368 if (ntp->int_value == type) { 369 (void) strlcpy(buf, ntp->int_name, len); 370 return; 371 } 372 } 373 374 (void) snprintf(buf, len, "0x%02x", type); 375 } 376 377 void 378 topo_led_type_name(uint8_t type, char *buf, size_t len) 379 { 380 topo_name_trans_t *ntp; 381 382 for (ntp = &topo_led_type_table[0]; ntp->int_name != NULL; ntp++) { 383 if (ntp->int_value == type) { 384 (void) strlcpy(buf, ntp->int_name, len); 385 return; 386 } 387 } 388 389 (void) snprintf(buf, len, "0x%02x", type); 390 } 391 392 void 393 topo_led_state_name(uint8_t type, char *buf, size_t len) 394 { 395 topo_name_trans_t *ntp; 396 397 for (ntp = &topo_led_states_table[0]; ntp->int_name != NULL; ntp++) { 398 if (ntp->int_value == type) { 399 (void) strlcpy(buf, ntp->int_name, len); 400 return; 401 } 402 } 403 404 (void) snprintf(buf, len, "0x%02x", type); 405 } 406 407 void 408 topo_sensor_state_name(uint32_t sensor_type, uint8_t state, char *buf, 409 size_t len) 410 { 411 topo_name_trans_t *ntp; 412 413 switch (sensor_type) { 414 case TOPO_SENSOR_TYPE_PHYSICAL: 415 ntp = &topo_sensor_states_physical_table[0]; 416 break; 417 case TOPO_SENSOR_TYPE_PLATFORM: 418 ntp = &topo_sensor_states_platform_table[0]; 419 break; 420 case TOPO_SENSOR_TYPE_PROCESSOR: 421 ntp = &topo_sensor_states_processor_table[0]; 422 break; 423 case TOPO_SENSOR_TYPE_POWER_SUPPLY: 424 ntp = &topo_sensor_states_power_supply_table[0]; 425 break; 426 case TOPO_SENSOR_TYPE_POWER_UNIT: 427 ntp = &topo_sensor_states_power_unit_table[0]; 428 break; 429 case TOPO_SENSOR_TYPE_MEMORY: 430 ntp = &topo_sensor_states_memory_table[0]; 431 break; 432 case TOPO_SENSOR_TYPE_BAY: 433 ntp = &topo_sensor_states_bay_table[0]; 434 break; 435 case TOPO_SENSOR_TYPE_FIRMWARE: 436 ntp = &topo_sensor_states_firmware_table[0]; 437 break; 438 case TOPO_SENSOR_TYPE_EVENT_LOG: 439 ntp = &topo_sensor_states_event_log_table[0]; 440 break; 441 case TOPO_SENSOR_TYPE_WATCHDOG1: 442 ntp = &topo_sensor_states_watchdog1_table[0]; 443 break; 444 case TOPO_SENSOR_TYPE_SYSTEM: 445 ntp = &topo_sensor_states_system_table[0]; 446 break; 447 case TOPO_SENSOR_TYPE_CRITICAL: 448 ntp = &topo_sensor_states_critical_table[0]; 449 break; 450 case TOPO_SENSOR_TYPE_BUTTON: 451 ntp = &topo_sensor_states_button_table[0]; 452 break; 453 case TOPO_SENSOR_TYPE_CABLE: 454 ntp = &topo_sensor_states_cable_table[0]; 455 break; 456 case TOPO_SENSOR_TYPE_BOOT_STATE: 457 ntp = &topo_sensor_states_boot_state_table[0]; 458 break; 459 case TOPO_SENSOR_TYPE_BOOT_ERROR: 460 ntp = &topo_sensor_states_boot_error_table[0]; 461 break; 462 case TOPO_SENSOR_TYPE_BOOT_OS: 463 ntp = &topo_sensor_states_boot_os_table[0]; 464 break; 465 case TOPO_SENSOR_TYPE_OS_SHUTDOWN: 466 ntp = &topo_sensor_states_os_table[0]; 467 break; 468 case TOPO_SENSOR_TYPE_SLOT: 469 ntp = &topo_sensor_states_slot_table[0]; 470 break; 471 case TOPO_SENSOR_TYPE_ACPI: 472 ntp = &topo_sensor_states_acpi_table[0]; 473 break; 474 case TOPO_SENSOR_TYPE_WATCHDOG2: 475 ntp = &topo_sensor_states_watchdog2_table[0]; 476 break; 477 case TOPO_SENSOR_TYPE_ALERT: 478 ntp = &topo_sensor_states_alert_table[0]; 479 break; 480 case TOPO_SENSOR_TYPE_PRESENCE: 481 ntp = &topo_sensor_states_presence_table[0]; 482 break; 483 case TOPO_SENSOR_TYPE_LAN: 484 ntp = &topo_sensor_states_lan_table[0]; 485 break; 486 case TOPO_SENSOR_TYPE_HEALTH: 487 ntp = &topo_sensor_states_health_table[0]; 488 break; 489 case TOPO_SENSOR_TYPE_BATTERY: 490 ntp = &topo_sensor_states_battery_table[0]; 491 break; 492 case TOPO_SENSOR_TYPE_AUDIT: 493 ntp = &topo_sensor_states_audit_table[0]; 494 break; 495 case TOPO_SENSOR_TYPE_VERSION: 496 ntp = &topo_sensor_states_version_table[0]; 497 break; 498 case TOPO_SENSOR_TYPE_FRU_STATE: 499 ntp = &topo_sensor_states_fru_state_table[0]; 500 break; 501 case TOPO_SENSOR_TYPE_THRESHOLD_STATE: 502 ntp = &topo_sensor_states_thresh_table[0]; 503 break; 504 case TOPO_SENSOR_TYPE_GENERIC_USAGE: 505 ntp = &topo_sensor_states_generic_usage_table[0]; 506 break; 507 case TOPO_SENSOR_TYPE_GENERIC_STATE: 508 ntp = &topo_sensor_states_generic_state_table[0]; 509 break; 510 case TOPO_SENSOR_TYPE_GENERIC_PREDFAIL: 511 ntp = &topo_sensor_states_generic_predfail_table[0]; 512 break; 513 case TOPO_SENSOR_TYPE_GENERIC_LIMIT: 514 ntp = &topo_sensor_states_generic_limit_table[0]; 515 break; 516 case TOPO_SENSOR_TYPE_GENERIC_PERFORMANCE: 517 ntp = &topo_sensor_states_generic_perf_table[0]; 518 break; 519 case TOPO_SENSOR_TYPE_SEVERITY: 520 ntp = &topo_sensor_states_severity_table[0]; 521 break; 522 case TOPO_SENSOR_TYPE_GENERIC_PRESENCE: 523 ntp = &topo_sensor_states_generic_presence_table[0]; 524 break; 525 case TOPO_SENSOR_TYPE_GENERIC_AVAILABILITY: 526 ntp = &topo_sensor_states_generic_avail_table[0]; 527 break; 528 case TOPO_SENSOR_TYPE_GENERIC_STATUS: 529 ntp = &topo_sensor_states_generic_status_table[0]; 530 break; 531 case TOPO_SENSOR_TYPE_GENERIC_ACPI: 532 ntp = &topo_sensor_states_generic_acpi_pwr_table[0]; 533 break; 534 case TOPO_SENSOR_TYPE_GENERIC_FAILURE: 535 ntp = &topo_sensor_states_generic_failure_table[0]; 536 break; 537 case TOPO_SENSOR_TYPE_GENERIC_OK: 538 ntp = &topo_sensor_states_generic_ok_table[0]; 539 break; 540 default: 541 (void) snprintf(buf, len, "0x%02x", state); 542 return; 543 } 544 if (state == 0) { 545 (void) snprintf(buf, len, "NO_STATES_ASSERTED"); 546 return; 547 } 548 buf[0] = '\0'; 549 for (; ntp->int_name != NULL; ntp++) { 550 if (state & ntp->int_value) { 551 if (buf[0] != '\0') 552 (void) strlcat(buf, "|", len); 553 (void) strlcat(buf, ntp->int_name, len); 554 } 555 } 556 557 if (buf[0] == '\0') 558 (void) snprintf(buf, len, "0x%02x", state); 559 } 560 561 static const topo_pgroup_info_t sys_pgroup = { 562 TOPO_PGROUP_SYSTEM, 563 TOPO_STABILITY_PRIVATE, 564 TOPO_STABILITY_PRIVATE, 565 1 566 }; 567 static const topo_pgroup_info_t auth_pgroup = { 568 FM_FMRI_AUTHORITY, 569 TOPO_STABILITY_PRIVATE, 570 TOPO_STABILITY_PRIVATE, 571 1 572 }; 573 574 void 575 topo_pgroup_hcset(tnode_t *node, nvlist_t *auth) 576 { 577 int err; 578 char isa[MAXNAMELEN]; 579 struct utsname uts; 580 char *prod, *psn, *csn, *server; 581 582 if (auth == NULL) 583 return; 584 585 if (topo_pgroup_create(node, &auth_pgroup, &err) != 0) { 586 if (err != ETOPO_PROP_DEFD) 587 return; 588 } 589 590 /* 591 * Inherit if we can, it saves memory 592 */ 593 if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT, 594 &err) != 0) && (err != ETOPO_PROP_DEFD)) { 595 if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, &prod) == 596 0) 597 (void) topo_prop_set_string(node, FM_FMRI_AUTHORITY, 598 FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod, 599 &err); 600 } 601 if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_PRODUCT_SN, 602 &err) != 0) && (err != ETOPO_PROP_DEFD)) { 603 if (nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN, &psn) == 604 0) 605 (void) topo_prop_set_string(node, FM_FMRI_AUTHORITY, 606 FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn, 607 &err); 608 } 609 if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS, 610 &err) != 0) && (err != ETOPO_PROP_DEFD)) { 611 if (nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS, &csn) == 0) 612 (void) topo_prop_set_string(node, FM_FMRI_AUTHORITY, 613 FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, csn, 614 &err); 615 } 616 if ((topo_prop_inherit(node, FM_FMRI_AUTHORITY, FM_FMRI_AUTH_SERVER, 617 &err) != 0) && (err != ETOPO_PROP_DEFD)) { 618 if (nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, &server) == 619 0) 620 (void) topo_prop_set_string(node, FM_FMRI_AUTHORITY, 621 FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server, 622 &err); 623 } 624 625 if (topo_pgroup_create(node, &sys_pgroup, &err) != 0) 626 return; 627 628 if (sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) != -1) 629 (void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, 630 TOPO_PROP_ISA, TOPO_PROP_IMMUTABLE, isa, &err); 631 632 if (uname(&uts) != -1) 633 (void) topo_prop_set_string(node, TOPO_PGROUP_SYSTEM, 634 TOPO_PROP_MACHINE, TOPO_PROP_IMMUTABLE, uts.machine, &err); 635 } 636