1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * prophist - property history utility 28 * 29 * 1. Description 30 * 31 * During the development of smf(5), a set of service manifests were delivered 32 * that required subsequent changes. The bulk of these changes are in ON, 33 * although additional consolidations may possess one or two manifests that are 34 * affected. These incorrect values need to be smoothed into a correct 35 * configuration surface for subsequent automatic merge technology to be 36 * introduced safely. The mechanism is the combination of this utility with a 37 * set of "property history" files. 38 * 39 * /var/svc/profile/prophist.SUNWcsr is delivered as an immutable file by the 40 * SUNWcsr packages. prophist.SUNWcsr covers the entire ON consolidation, for 41 * the purposes of collecting in one place what is essentially a temporary 42 * construct. Other consolidations should deliver /var/svc/profile/prophist.* 43 * files. 44 * 45 * The processing of the property history files occurs in 46 * svc:/system/manifest-import:default. Each prophist.* file is checked against 47 * its hashed value in smf/manifest using the "hash" subcommand. If a change is 48 * detected, the prophist.* file is sourced. These operations are carried out 49 * prior to any manifest being imported. 50 * 51 * 2. Interface 52 * 53 * prophist presents a subcommand style interface, with various suboptions to 54 * each subcommand: 55 * 56 * prophist delete -e FMRI -g pg [-p prop] 57 * prophist upgrade -e FMRI -g pg -p prop -n newval oldval ... 58 * prophist overwrite -e FMRI -g pg -p prop -n newval 59 * prophist hash file 60 * 61 * The hash subcommand signals that a file requires processing using an exit 62 * status of 3. Otherwise, exit statuses of 0, 1, and 2 have their conventional 63 * meaning. 64 * 65 * 3. Limitations 66 * 67 * The present implementation has no support for multiply-valued properties. 68 * Manipulation of such properties should be done using a svccfg(1M) invocation 69 * in the appropriate prophist.* file. 70 */ 71 72 #include <sys/types.h> 73 74 #include <assert.h> 75 #include <libintl.h> 76 #include <libscf.h> 77 #include <libscf_priv.h> 78 #include <libuutil.h> 79 #include <locale.h> 80 #include <stdio.h> 81 #include <stdlib.h> 82 #include <string.h> 83 #include <strings.h> 84 #include <unistd.h> 85 86 #include <manifest_hash.h> 87 88 #define OPTIONS_STR "e:g:n:p:" 89 90 static int o_delete; 91 static int o_hash; 92 static int o_overwrite; 93 94 static char *entity; 95 static char *pgrp_name; 96 static char *prop_name; 97 static char *new_value; 98 99 static scf_handle_t *hndl; 100 static scf_service_t *svc; 101 static scf_instance_t *inst; 102 static scf_snapshot_t *snap; 103 static scf_snaplevel_t *level; 104 static scf_propertygroup_t *pg; 105 static scf_property_t *prop; 106 static scf_value_t *value; 107 static scf_iter_t *iter; 108 static scf_transaction_t *tx; 109 static scf_transaction_entry_t *entry; 110 111 static scf_type_t ptype; 112 113 static char *valbuf; 114 static ssize_t valbuf_sz; 115 116 #define LG_BUFSIZ 1024 /* larger than a property name */ 117 static char namebuf[LG_BUFSIZ]; 118 119 static void 120 usage() 121 { 122 (void) fprintf(stderr, gettext( 123 "Usage:" 124 "\tprophist hash file\n" 125 "\tprophist delete -e FMRI -g pg [-p prop]\n" 126 "\tprophist overwrite -e FMRI -g pg -p prop -n newval\n" 127 "\tprophist upgrade -e FMRI -g pg -p prop -n newval oldval " 128 "...\n")); 129 exit(UU_EXIT_USAGE); 130 } 131 132 static void 133 ready_scf_objects() 134 { 135 if ((hndl = scf_handle_create(SCF_VERSION)) == NULL) 136 uu_die(gettext("handle creation failed: %s\n"), 137 scf_strerror(scf_error())); 138 139 if (scf_handle_bind(hndl) != 0) 140 uu_die(gettext("handle bind failed: %s\n"), 141 scf_strerror(scf_error())); 142 143 svc = scf_service_create(hndl); 144 inst = scf_instance_create(hndl); 145 snap = scf_snapshot_create(hndl); 146 level = scf_snaplevel_create(hndl); 147 pg = scf_pg_create(hndl); 148 prop = scf_property_create(hndl); 149 value = scf_value_create(hndl); 150 iter = scf_iter_create(hndl); 151 tx = scf_transaction_create(hndl); 152 entry = scf_entry_create(hndl); 153 154 if (svc == NULL || 155 inst == NULL || 156 snap == NULL || 157 level == NULL || 158 pg == NULL || 159 prop == NULL || 160 value == NULL || 161 iter == NULL || 162 tx == NULL || 163 entry == NULL) 164 uu_die(gettext("object creation failed: %s\n"), 165 scf_strerror(scf_error())); 166 167 valbuf_sz = 4096; 168 valbuf = malloc(valbuf_sz); 169 if (valbuf == NULL) 170 uu_die(gettext("value buffer allocation failed")); 171 } 172 173 static int 174 hash(char *arg) 175 { 176 char *pname; 177 char *errstr; 178 int ret; 179 uchar_t hash[MHASH_SIZE]; 180 181 ready_scf_objects(); 182 183 switch (ret = mhash_test_file(hndl, arg, 0, &pname, hash)) { 184 case MHASH_RECONCILED: 185 /* Equivalent hash already stored. */ 186 return (0); 187 case MHASH_NEWFILE: 188 /* Hash differs. */ 189 break; 190 case MHASH_FAILURE: 191 uu_die(gettext("mhash_test_file() failed")); 192 default: 193 uu_die(gettext("unknown return value (%d) from " 194 "mhash_test_file()"), ret); 195 } 196 197 if (mhash_store_entry(hndl, pname, arg, hash, &errstr)) { 198 if (errstr) 199 uu_die(errstr); 200 else 201 uu_die(gettext("Unknown error from " 202 "mhash_store_entry()\n")); 203 } 204 205 return (3); 206 } 207 208 static int 209 delete_prop(scf_propertygroup_t *pg, char *prop_name) 210 { 211 if (scf_transaction_start(tx, pg) != 0) 212 uu_die(gettext("transaction start failed: %s\n"), 213 scf_strerror(scf_error())); 214 if (scf_transaction_property_delete(tx, entry, prop_name) != 0) 215 uu_die(gettext("transaction property delete failed: %s\n"), 216 scf_strerror(scf_error())); 217 if (scf_transaction_commit(tx) != 1) 218 return (1); 219 220 return (0); 221 } 222 223 /* 224 * Returns 1 if target property group or property not found. 225 */ 226 static int 227 delete_pg_or_prop(scf_iter_t *pg_iter, char *pgrp_name, char *prop_name) 228 { 229 while (scf_iter_next_pg(pg_iter, pg) > 0) { 230 if (scf_pg_get_name(pg, namebuf, LG_BUFSIZ) == -1) 231 continue; 232 233 if (strcmp(namebuf, pgrp_name) != 0) 234 continue; 235 236 if (prop_name != NULL) 237 return (delete_prop(pg, prop_name)); 238 239 if (scf_pg_delete(pg) != 0) 240 uu_die(gettext("property group delete failed: %s\n"), 241 scf_strerror(scf_error())); 242 243 return (0); 244 } 245 246 return (1); 247 } 248 249 /* 250 * Remove property group or property from both service and instance. 251 */ 252 static int 253 delete(char *entity, char *pgrp_name, char *prop_name) 254 { 255 ready_scf_objects(); 256 257 if (scf_handle_decode_fmri(hndl, entity, NULL, svc, inst, NULL, NULL, 258 SCF_DECODE_FMRI_EXACT) == 0) { 259 (void) scf_iter_instance_pgs(iter, inst); 260 return (delete_pg_or_prop(iter, pgrp_name, prop_name)); 261 } 262 263 if (scf_handle_decode_fmri(hndl, entity, NULL, svc, NULL, NULL, 264 NULL, SCF_DECODE_FMRI_EXACT) == 0) { 265 (void) scf_iter_service_pgs(iter, svc); 266 return (delete_pg_or_prop(iter, pgrp_name, prop_name)); 267 } 268 269 uu_die(gettext("%s not decoded: %s\n"), entity, 270 scf_strerror(scf_error())); 271 272 /*NOTREACHED*/ 273 } 274 275 static void 276 replace_value(scf_propertygroup_t *pg, char *prop_name, char *new_value) 277 { 278 int result; 279 int ret; 280 281 do { 282 if (scf_pg_update(pg) == -1) 283 uu_die(gettext("property group update failed: %s\n"), 284 scf_strerror(scf_error())); 285 if (scf_transaction_start(tx, pg) != SCF_SUCCESS) { 286 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) 287 uu_die(gettext("permission denied\n")); 288 289 uu_die(gettext("transaction start failed: %s\n"), 290 scf_strerror(scf_error())); 291 } 292 293 ret = scf_pg_get_property(pg, prop_name, prop); 294 if (ret == SCF_SUCCESS) { 295 if (scf_property_type(prop, &ptype) != SCF_SUCCESS) 296 uu_die(gettext("couldn't get property type\n")); 297 if (scf_transaction_property_change_type(tx, entry, 298 prop_name, ptype) == -1) 299 uu_die(gettext("couldn't change entry\n")); 300 } else if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) { 301 uu_die(gettext("illegal property name\n")); 302 } else { 303 uu_die(gettext("property fetch failed\n")); 304 } 305 306 if (scf_value_set_from_string(value, ptype, 307 (const char *)new_value) != 0) { 308 assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT); 309 uu_die(gettext("Invalid \"%s\" value \"%s\".\n"), 310 scf_type_to_string(ptype), new_value); 311 } 312 313 ret = scf_entry_add_value(entry, value); 314 if (ret != SCF_SUCCESS) 315 uu_die(gettext("scf_entry_add_value failed: %s\n"), 316 scf_strerror(scf_error())); 317 318 assert(ret == SCF_SUCCESS); 319 320 result = scf_transaction_commit(tx); 321 322 scf_transaction_reset(tx); 323 scf_entry_destroy_children(entry); 324 } while (result == 0); 325 326 if (result < 0) { 327 if (scf_error() != SCF_ERROR_PERMISSION_DENIED) 328 uu_die(gettext("transaction commit failed: %s\n"), 329 scf_strerror(scf_error())); 330 331 uu_die(gettext("permission denied\n")); 332 } 333 } 334 335 static scf_propertygroup_t * 336 get_pg(char *entity, char *pgrp_name, char *prop_name) 337 { 338 scf_propertygroup_t *targetpg; 339 340 ready_scf_objects(); 341 342 if (scf_handle_decode_fmri(hndl, entity, NULL, svc, inst, NULL, NULL, 343 SCF_DECODE_FMRI_EXACT) == 0) { 344 /* 345 * 1. Working at the instance level. The instance level 346 * contains one special case: general/enabled is active in the 347 * current version, and its value in snapshots is not relevant. 348 * Otherwise, pull from running snapshot. 349 */ 350 if (strcmp(pgrp_name, "general") == 0 && 351 strcmp(prop_name, "enabled") == 0) { 352 if (scf_instance_get_pg(inst, pgrp_name, pg) == 0) 353 return (pg); 354 355 uu_die(gettext("property group %s not available: %s\n"), 356 pgrp_name, scf_strerror(scf_error())); 357 } 358 359 if (scf_instance_get_snapshot(inst, "running", snap) == -1) { 360 if (scf_instance_get_pg(inst, pgrp_name, pg) == 0) 361 return (pg); 362 363 uu_die(gettext("property group %s not available: %s\n"), 364 pgrp_name, scf_strerror(scf_error())); 365 } 366 367 if (scf_snapshot_get_base_snaplevel(snap, level) != 0) 368 uu_die(gettext("base snaplevel not available: %s\n"), 369 scf_strerror(scf_error())); 370 371 if (scf_snaplevel_get_pg(level, pgrp_name, pg) == -1) 372 uu_die(gettext("property group %s not available: %s\n"), 373 pgrp_name, scf_strerror(scf_error())); 374 375 targetpg = scf_pg_create(hndl); 376 if (scf_instance_get_pg(inst, pgrp_name, targetpg) == -1) 377 uu_die(gettext("property group %s not available: %s\n"), 378 pgrp_name, scf_strerror(scf_error())); 379 380 return (targetpg); 381 } 382 383 if (scf_handle_decode_fmri(hndl, entity, NULL, svc, NULL, NULL, 384 NULL, SCF_DECODE_FMRI_EXACT) == 0) { 385 /* 386 * 2. Working at the service level. 387 */ 388 if (scf_service_get_pg(svc, pgrp_name, pg) == 0) 389 return (pg); 390 391 uu_die(gettext("property group %s not available: %s\n"), 392 pgrp_name, scf_strerror(scf_error())); 393 } 394 395 /* 396 * 3. Cannot decode either instance or service exactly. 397 */ 398 uu_die(gettext("%s not decoded: %s\n"), entity, 399 scf_strerror(scf_error())); 400 401 /*NOTREACHED*/ 402 } 403 404 static int 405 upgrade(char *entity, char *pgrp_name, char *prop_name, char *new_value, 406 int argc, char *argv[], int optind) 407 { 408 int replace = 0; 409 int vals = 0; 410 scf_propertygroup_t *targetpg; 411 412 targetpg = get_pg(entity, pgrp_name, prop_name); 413 414 if (scf_pg_get_property(targetpg, prop_name, prop) != 0) 415 uu_die(gettext("property %s/%s not available: %s\n"), pgrp_name, 416 prop_name, scf_strerror(scf_error())); 417 418 if (scf_iter_property_values(iter, prop) != 0) 419 uu_die(gettext("could not establish value iterator: %s\n"), 420 scf_strerror(scf_error())); 421 422 while (scf_iter_next_value(iter, value) == 1) { 423 if (scf_value_get_as_string(value, valbuf, valbuf_sz) < 0) 424 uu_die(gettext("string value get failed: %s\n"), 425 scf_strerror(scf_error())); 426 427 for (; optind < argc; optind++) 428 if (strcmp(valbuf, argv[optind]) == 0) { 429 replace = 1; 430 break; 431 } 432 433 vals++; 434 if (vals > 1) 435 uu_die(gettext("too many values to upgrade\n")); 436 } 437 438 if (replace) 439 replace_value(targetpg, prop_name, new_value); 440 441 return (0); 442 } 443 444 static int 445 overwrite(char *entity, char *pgrp_name, char *prop_name, char *new_value) 446 { 447 scf_propertygroup_t *targetpg; 448 449 targetpg = get_pg(entity, pgrp_name, prop_name); 450 451 if (scf_pg_get_property(targetpg, prop_name, prop) != 0) 452 uu_die(gettext("property %s/%s not available: %s\n"), pgrp_name, 453 prop_name, scf_strerror(scf_error())); 454 455 replace_value(targetpg, prop_name, new_value); 456 457 return (0); 458 } 459 460 int 461 main(int argc, char *argv[]) 462 { 463 int c; 464 465 if (argc < 2) 466 usage(); 467 468 if (strcmp(argv[1], "hash") == 0) 469 o_hash = 1; 470 else if (strcmp(argv[1], "delete") == 0) 471 o_delete = 1; 472 else if (strcmp(argv[1], "overwrite") == 0) 473 o_overwrite = 1; 474 else if (strcmp(argv[1], "upgrade") != 0) 475 usage(); 476 477 (void) uu_setpname(argv[0]); 478 479 argv++; 480 argc--; 481 482 while ((c = getopt(argc, argv, OPTIONS_STR)) != EOF) { 483 switch (c) { 484 case 'e': 485 entity = optarg; 486 break; 487 case 'g': 488 pgrp_name = optarg; 489 break; 490 case 'n': 491 new_value = optarg; 492 break; 493 case 'p': 494 prop_name = optarg; 495 break; 496 case '?': 497 default: 498 usage(); 499 break; 500 } 501 } 502 503 if (o_hash) { 504 if (entity != NULL || 505 pgrp_name != NULL || 506 prop_name != NULL || 507 new_value != NULL) 508 usage(); 509 510 return (hash(argv[optind])); 511 } 512 513 if (entity == NULL) 514 usage(); 515 516 if (o_delete) { 517 if (pgrp_name == NULL || 518 new_value != NULL || 519 optind < argc) 520 usage(); 521 522 return (delete(entity, pgrp_name, prop_name)); 523 } 524 525 if (pgrp_name == NULL || prop_name == NULL || new_value == NULL) 526 usage(); 527 528 if (o_overwrite) 529 return (overwrite(entity, pgrp_name, prop_name, new_value)); 530 531 if (optind >= argc) 532 usage(); 533 534 return (upgrade(entity, pgrp_name, prop_name, new_value, argc, argv, 535 optind)); 536 } 537