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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011, Joyent, Inc. All rights reserved.
25 * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
26 */
27
28 /*
29 * svcs - display attributes of service instances
30 *
31 * We have two output formats and six instance selection mechanisms. The
32 * primary output format is a line of attributes (selected by -o), possibly
33 * followed by process description lines (if -p is specified), for each
34 * instance selected. The columns available to display are described by the
35 * struct column columns array. The columns to actually display are kept in
36 * the opt_columns array as indicies into the columns array. The selection
37 * mechanisms available for this format are service FMRIs (selects all child
38 * instances), instance FMRIs, instance FMRI glob patterns, instances with
39 * a certain restarter (-R), dependencies of instances (-d), and dependents of
40 * instances (-D). Since the lines must be sorted (per -sS), we'll just stick
41 * each into a data structure and print them in order when we're done. To
42 * avoid listing the same instance twice (when -d and -D aren't given), we'll
43 * use a hash table of FMRIs to record that we've listed (added to the tree)
44 * an instance.
129 ssize_t max_scf_value_length;
130 ssize_t max_scf_fmri_length;
131 static ssize_t max_scf_type_length;
132 static time_t now;
133 static struct pfmri_list *restarters = NULL;
134 static int first_paragraph = 1; /* For -l mode. */
135 static char *common_name_buf; /* Sized for maximal length value. */
136 char *locale; /* Current locale. */
137 char *g_zonename; /* zone being operated upon */
138
139 /*
140 * Pathname storage for path generated from the fmri.
141 * Used for reading the ctid and (start) pid files for an inetd service.
142 */
143 static char genfmri_filename[MAXPATHLEN] = "";
144
145 /* Options */
146 static int *opt_columns = NULL; /* Indices into columns to display. */
147 static int opt_cnum = 0;
148 static int opt_processes = 0; /* Print processes? */
149 static int *opt_sort = NULL; /* Indices into columns to sort. */
150 static int opt_snum = 0;
151 static int opt_nstate_shown = 0; /* Will nstate be shown? */
152 static int opt_verbose = 0;
153 static char *opt_zone; /* zone selected, if any */
154
155 /* Minimize string constants. */
156 static const char * const scf_property_state = SCF_PROPERTY_STATE;
157 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
158 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
159
160
161 /*
162 * Utility functions
163 */
164
165 /*
166 * For unexpected libscf errors. The ending newline is necessary to keep
167 * uu_die() from appending the errno error.
168 */
984
985 /*
986 * This function should write sortkey_width bytes into buf which will
987 * cause memcmp() to sort it properly. (Unlike sprint() above,
988 * however, an extra null byte may overrun the buffer.) The second
989 * argument controls whether the results are sorted in forward or
990 * reverse order.
991 */
992 void (*get_sortkey)(char *, int, scf_walkinfo_t *);
993 };
994
995 static void
996 reverse_bytes(char *buf, size_t len)
997 {
998 int i;
999
1000 for (i = 0; i < len; ++i)
1001 buf[i] = ~buf[i];
1002 }
1003
1004 /* CTID */
1005 #define CTID_COLUMN_WIDTH 6
1006 #define CTID_COLUMN_BUFSIZE 20 /* max ctid_t + space + \0 */
1007
1008 static void
1009 sprint_ctid(char **buf, scf_walkinfo_t *wip)
1010 {
1011 int r;
1012 uint64_t c;
1013 size_t newsize = (*buf ? strlen(*buf) : 0) + CTID_COLUMN_BUFSIZE;
1014 char *newbuf = safe_malloc(newsize);
1015 int restarter_spec;
1016
1017 /*
1018 * Use the restarter specific get pids routine, if available.
1019 * Only check for non-legacy services (wip->pg == 0).
1020 */
1021 if (wip->pg != NULL) {
1022 r = pg_get_single_val(wip->pg, scf_property_contract,
1023 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
1024 } else {
1025 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1026 NULL, NULL);
1027 if (restarter_spec == 0) {
1028 /* No restarter specific routine */
1029 r = get_restarter_count_prop(wip->inst,
1030 scf_property_contract, &c, EMPTY_OK | MULTI_OK);
1031 }
1032 }
1033
1034 if (r == 0)
1035 (void) snprintf(newbuf, newsize, "%s%*lu ",
1036 *buf ? *buf : "", CTID_COLUMN_WIDTH, (ctid_t)c);
1037 else if (r == E2BIG)
1038 (void) snprintf(newbuf, newsize, "%s%*lu* ",
1039 *buf ? *buf : "", CTID_COLUMN_WIDTH - 1, (ctid_t)c);
1040 else
1041 (void) snprintf(newbuf, newsize, "%s%*s ",
1042 *buf ? *buf : "", CTID_COLUMN_WIDTH, "-");
1043 if (*buf)
1044 free(*buf);
1045 *buf = newbuf;
1046 }
1047
1048 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
1049
1050 static void
1051 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
1052 {
1053 int r;
1054 uint64_t c;
1055 int restarter_spec;
1056
1057 /*
1058 * Use the restarter specific get pids routine, if available.
1059 * Only check for non-legacy services (wip->pg == 0).
1060 */
1061 if (wip->pg != NULL) {
1062 r = pg_get_single_val(wip->pg, scf_property_contract,
1063 SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
1064 } else {
1065 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1077 * work.
1078 */
1079 c = BE_64(c);
1080
1081 bcopy(&c, buf, CTID_SORTKEY_WIDTH);
1082 } else {
1083 bzero(buf, CTID_SORTKEY_WIDTH);
1084 }
1085
1086 if (reverse)
1087 reverse_bytes(buf, CTID_SORTKEY_WIDTH);
1088 }
1089
1090 /* DESC */
1091 #define DESC_COLUMN_WIDTH 100
1092
1093 static void
1094 sprint_desc(char **buf, scf_walkinfo_t *wip)
1095 {
1096 char *x;
1097 size_t newsize;
1098 char *newbuf;
1099
1100 if (common_name_buf == NULL)
1101 common_name_buf = safe_malloc(max_scf_value_length + 1);
1102
1103 bzero(common_name_buf, max_scf_value_length + 1);
1104
1105 if (wip->pg != NULL) {
1106 common_name_buf[0] = '-';
1107 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
1108 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1109 1, 1) == -1 &&
1110 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
1111 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1112 1, 1) == -1) {
1113 common_name_buf[0] = '-';
1114 }
1115
1116 /*
1117 * Collapse multi-line tm_common_name values into a single line.
1118 */
1119 for (x = common_name_buf; *x != '\0'; x++)
1120 if (*x == '\n')
1121 *x = ' ';
1122
1123 if (strlen(common_name_buf) > DESC_COLUMN_WIDTH)
1124 newsize = (*buf ? strlen(*buf) : 0) +
1125 strlen(common_name_buf) + 1;
1126 else
1127 newsize = (*buf ? strlen(*buf) : 0) + DESC_COLUMN_WIDTH + 1;
1128 newbuf = safe_malloc(newsize);
1129 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1130 DESC_COLUMN_WIDTH, common_name_buf);
1131 if (*buf)
1132 free(*buf);
1133 *buf = newbuf;
1134 }
1135
1136 /* ARGSUSED */
1137 static void
1138 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
1139 {
1140 bzero(buf, DESC_COLUMN_WIDTH);
1141 }
1142
1143 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
1144
1145 static char
1146 state_to_char(const char *state)
1147 {
1148 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1149 return ('u');
1150
1151 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1152 return ('0');
1153
1203 *buf = 4;
1204 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
1205 *buf = 5;
1206 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
1207 *buf = 1;
1208 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
1209 *buf = 6;
1210 else
1211 *buf = 7;
1212 } else
1213 *buf = 0;
1214
1215 if (reverse)
1216 *buf = 255 - *buf;
1217 }
1218
1219 static void
1220 sprint_state(char **buf, scf_walkinfo_t *wip)
1221 {
1222 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1223 size_t newsize;
1224 char *newbuf;
1225
1226 if (wip->pg == NULL) {
1227 get_restarter_string_prop(wip->inst, scf_property_state,
1228 state_name, sizeof (state_name));
1229
1230 /* Don't print blank fields, to ease parsing. */
1231 if (state_name[0] == '\0') {
1232 state_name[0] = '-';
1233 state_name[1] = '\0';
1234 }
1235
1236 if (!opt_nstate_shown && transitioning(wip->inst)) {
1237 /* Append an asterisk if nstate is valid. */
1238 (void) strcat(state_name, "*");
1239 }
1240 } else
1241 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1242
1243 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 2;
1244 newbuf = safe_malloc(newsize);
1245 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1246 MAX_SCF_STATE_STRING_SZ + 1, state_name);
1247
1248 if (*buf)
1249 free(*buf);
1250 *buf = newbuf;
1251 }
1252
1253 static void
1254 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
1255 {
1256 sortkey_states(scf_property_state, buf, reverse, wip);
1257 }
1258
1259 static void
1260 sprint_nstate(char **buf, scf_walkinfo_t *wip)
1261 {
1262 char next_state_name[MAX_SCF_STATE_STRING_SZ];
1263 boolean_t blank = 0;
1264 size_t newsize;
1265 char *newbuf;
1266
1267 if (wip->pg == NULL) {
1268 get_restarter_string_prop(wip->inst, scf_property_next_state,
1269 next_state_name, sizeof (next_state_name));
1270
1271 /* Don't print blank fields, to ease parsing. */
1272 if (next_state_name[0] == '\0' ||
1273 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
1274 blank = 1;
1275 } else
1276 blank = 1;
1277
1278 if (blank) {
1279 next_state_name[0] = '-';
1280 next_state_name[1] = '\0';
1281 }
1282
1283 newsize = (*buf ? strlen(*buf) : 0) + MAX_SCF_STATE_STRING_SZ + 1;
1284 newbuf = safe_malloc(newsize);
1285 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1286 MAX_SCF_STATE_STRING_SZ - 1, next_state_name);
1287 if (*buf)
1288 free(*buf);
1289 *buf = newbuf;
1290 }
1291
1292 static void
1293 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
1294 {
1295 sortkey_states(scf_property_next_state, buf, reverse, wip);
1296 }
1297
1298 static void
1299 sprint_s(char **buf, scf_walkinfo_t *wip)
1300 {
1301 char tmp[3];
1302 char state_name[MAX_SCF_STATE_STRING_SZ];
1303 size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
1304 char *newbuf = safe_malloc(newsize);
1305
1306 if (wip->pg == NULL) {
1307 get_restarter_string_prop(wip->inst, scf_property_state,
1308 state_name, sizeof (state_name));
1309 tmp[0] = state_to_char(state_name);
1310
1311 if (!opt_nstate_shown && transitioning(wip->inst))
1312 tmp[1] = '*';
1313 else
1314 tmp[1] = ' ';
1315 } else {
1316 tmp[0] = 'L';
1317 tmp[1] = ' ';
1318 }
1319 tmp[2] = ' ';
1320 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
1321 3, tmp);
1322 if (*buf)
1323 free(*buf);
1324 *buf = newbuf;
1325 }
1326
1327 static void
1328 sprint_n(char **buf, scf_walkinfo_t *wip)
1329 {
1330 char tmp[2];
1331 size_t newsize = (*buf ? strlen(*buf) : 0) + 3;
1332 char *newbuf = safe_malloc(newsize);
1333 char nstate_name[MAX_SCF_STATE_STRING_SZ];
1334
1335 if (wip->pg == NULL) {
1336 get_restarter_string_prop(wip->inst, scf_property_next_state,
1337 nstate_name, sizeof (nstate_name));
1338
1339 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1340 tmp[0] = '-';
1341 else
1342 tmp[0] = state_to_char(nstate_name);
1343 } else
1344 tmp[0] = '-';
1345
1346 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1347 2, tmp);
1348 if (*buf)
1349 free(*buf);
1350 *buf = newbuf;
1351 }
1352
1353 static void
1354 sprint_sn(char **buf, scf_walkinfo_t *wip)
1355 {
1356 char tmp[3];
1357 size_t newsize = (*buf ? strlen(*buf) : 0) + 4;
1358 char *newbuf = safe_malloc(newsize);
1359 char nstate_name[MAX_SCF_STATE_STRING_SZ];
1360 char state_name[MAX_SCF_STATE_STRING_SZ];
1361
1362 if (wip->pg == NULL) {
1363 get_restarter_string_prop(wip->inst, scf_property_state,
1364 state_name, sizeof (state_name));
1365 get_restarter_string_prop(wip->inst, scf_property_next_state,
1366 nstate_name, sizeof (nstate_name));
1367 tmp[0] = state_to_char(state_name);
1368
1369 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1370 tmp[1] = '-';
1371 else
1372 tmp[1] = state_to_char(nstate_name);
1373 } else {
1374 tmp[0] = 'L';
1375 tmp[1] = '-';
1376 }
1377
1378 tmp[2] = ' ';
1379 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1380 3, tmp);
1381 if (*buf)
1382 free(*buf);
1383 *buf = newbuf;
1384 }
1385
1386 /* ARGSUSED */
1387 static void
1388 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
1389 {
1390 sortkey_state(buf, reverse, wip);
1391 sortkey_nstate(buf + 1, reverse, wip);
1392 }
1393
1394 static const char *
1395 state_abbrev(const char *state)
1396 {
1397 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1398 return ("UN");
1399 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1400 return ("OFF");
1401 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1402 return ("ON");
1403 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1404 return ("MNT");
1405 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1406 return ("DIS");
1407 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1408 return ("DGD");
1409 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1410 return ("LRC");
1411
1412 return ("?");
1413 }
1414
1415 static void
1416 sprint_sta(char **buf, scf_walkinfo_t *wip)
1417 {
1418 char state_name[MAX_SCF_STATE_STRING_SZ];
1419 char sta[5];
1420 size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
1421 char *newbuf = safe_malloc(newsize);
1422
1423 if (wip->pg == NULL)
1424 get_restarter_string_prop(wip->inst, scf_property_state,
1425 state_name, sizeof (state_name));
1426 else
1427 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1428
1429 (void) strcpy(sta, state_abbrev(state_name));
1430
1431 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
1432 (void) strcat(sta, "*");
1433
1434 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "", sta);
1435 if (*buf)
1436 free(*buf);
1437 *buf = newbuf;
1438 }
1439
1440 static void
1441 sprint_nsta(char **buf, scf_walkinfo_t *wip)
1442 {
1443 char state_name[MAX_SCF_STATE_STRING_SZ];
1444 size_t newsize = (*buf ? strlen(*buf) : 0) + 6;
1445 char *newbuf = safe_malloc(newsize);
1446
1447 if (wip->pg == NULL)
1448 get_restarter_string_prop(wip->inst, scf_property_next_state,
1449 state_name, sizeof (state_name));
1450 else
1451 (void) strcpy(state_name, SCF_STATE_STRING_NONE);
1452
1453 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
1454 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
1455 "-");
1456 else
1457 (void) snprintf(newbuf, newsize, "%s%-4s ", *buf ? *buf : "",
1458 state_abbrev(state_name));
1459 if (*buf)
1460 free(*buf);
1461 *buf = newbuf;
1462 }
1463
1464 /* FMRI */
1465 #define FMRI_COLUMN_WIDTH 50
1466 static void
1467 sprint_fmri(char **buf, scf_walkinfo_t *wip)
1468 {
1469 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
1470 size_t newsize;
1471 char *newbuf;
1472
1473 if (wip->pg == NULL) {
1474 if (scf_instance_to_fmri(wip->inst, fmri_buf,
1475 max_scf_fmri_length + 1) == -1)
1476 scfdie();
1477 } else {
1478 (void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
1479 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
1480 SCF_TYPE_ASTRING, fmri_buf +
1481 sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
1482 max_scf_fmri_length + 1 -
1483 (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
1484 (void) strcat(fmri_buf, LEGACY_UNKNOWN);
1485 }
1486
1487 if (strlen(fmri_buf) > FMRI_COLUMN_WIDTH)
1488 newsize = (*buf ? strlen(*buf) : 0) + strlen(fmri_buf) + 2;
1489 else
1490 newsize = (*buf ? strlen(*buf) : 0) + FMRI_COLUMN_WIDTH + 2;
1491 newbuf = safe_malloc(newsize);
1492 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1493 FMRI_COLUMN_WIDTH, fmri_buf);
1494 free(fmri_buf);
1495 if (*buf)
1496 free(*buf);
1497 *buf = newbuf;
1498 }
1499
1500 static void
1501 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
1502 {
1503 char *tmp = NULL;
1504
1505 sprint_fmri(&tmp, wip);
1506 bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
1507 free(tmp);
1508 if (reverse)
1509 reverse_bytes(buf, FMRI_COLUMN_WIDTH);
1510 }
1511
1512 /* Component columns */
1513 #define COMPONENT_COLUMN_WIDTH 20
1514 static void
1515 sprint_scope(char **buf, scf_walkinfo_t *wip)
1516 {
1517 char *scope_buf = safe_malloc(max_scf_name_length + 1);
1518 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
1519 char *newbuf = safe_malloc(newsize);
1520
1521 assert(wip->scope != NULL);
1522
1523 if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
1524 scfdie();
1525
1526 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1527 COMPONENT_COLUMN_WIDTH, scope_buf);
1528 if (*buf)
1529 free(*buf);
1530 *buf = newbuf;
1531 free(scope_buf);
1532 }
1533
1534 static void
1535 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
1536 {
1537 char *tmp = NULL;
1538
1539 sprint_scope(&tmp, wip);
1540 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1541 free(tmp);
1542 if (reverse)
1543 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1544 }
1545
1546 static void
1547 sprint_service(char **buf, scf_walkinfo_t *wip)
1548 {
1549 char *svc_buf = safe_malloc(max_scf_name_length + 1);
1550 char *newbuf;
1551 size_t newsize;
1552
1553 if (wip->pg == NULL) {
1554 if (scf_service_get_name(wip->svc, svc_buf,
1555 max_scf_name_length + 1) < 0)
1556 scfdie();
1557 } else {
1558 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
1559 svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
1560 (void) strcpy(svc_buf, LEGACY_UNKNOWN);
1561 }
1562
1563
1564 if (strlen(svc_buf) > COMPONENT_COLUMN_WIDTH)
1565 newsize = (*buf ? strlen(*buf) : 0) + strlen(svc_buf) + 2;
1566 else
1567 newsize = (*buf ? strlen(*buf) : 0) +
1568 COMPONENT_COLUMN_WIDTH + 2;
1569 newbuf = safe_malloc(newsize);
1570 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1571 COMPONENT_COLUMN_WIDTH, svc_buf);
1572 free(svc_buf);
1573 if (*buf)
1574 free(*buf);
1575 *buf = newbuf;
1576 }
1577
1578 static void
1579 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
1580 {
1581 char *tmp = NULL;
1582
1583 sprint_service(&tmp, wip);
1584 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1585 free(tmp);
1586 if (reverse)
1587 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1588 }
1589
1590 /* INST */
1591 static void
1592 sprint_instance(char **buf, scf_walkinfo_t *wip)
1593 {
1594 char *tmp = safe_malloc(max_scf_name_length + 1);
1595 size_t newsize = (*buf ? strlen(*buf) : 0) + COMPONENT_COLUMN_WIDTH + 2;
1596 char *newbuf = safe_malloc(newsize);
1597
1598 if (wip->pg == NULL) {
1599 if (scf_instance_get_name(wip->inst, tmp,
1600 max_scf_name_length + 1) < 0)
1601 scfdie();
1602 } else {
1603 tmp[0] = '-';
1604 tmp[1] = '\0';
1605 }
1606
1607 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1608 COMPONENT_COLUMN_WIDTH, tmp);
1609 if (*buf)
1610 free(*buf);
1611 *buf = newbuf;
1612 free(tmp);
1613 }
1614
1615 static void
1616 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
1617 {
1618 char *tmp = NULL;
1619
1620 sprint_instance(&tmp, wip);
1621 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1622 free(tmp);
1623 if (reverse)
1624 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1625 }
1626
1627 /* STIME */
1628 #define STIME_COLUMN_WIDTH 8
1629 #define FORMAT_TIME "%k:%M:%S"
1630 #define FORMAT_DATE "%b_%d "
1631 #define FORMAT_YEAR "%Y "
1632
1633 /*
1634 * sprint_stime() will allocate a new buffer and snprintf the services's
1635 * state timestamp. If the timestamp is unavailable for some reason
1636 * a '-' is given instead.
1637 */
1638 static void
1639 sprint_stime(char **buf, scf_walkinfo_t *wip)
1640 {
1641 int r;
1642 struct timeval tv;
1643 time_t then;
1644 struct tm *tm;
1645 char st_buf[STIME_COLUMN_WIDTH + 1];
1646 size_t newsize = (*buf ? strlen(*buf) : 0) + STIME_COLUMN_WIDTH + 2;
1647 char *newbuf = safe_malloc(newsize);
1648
1649 if (wip->pg == NULL) {
1650 r = get_restarter_time_prop(wip->inst,
1651 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1652 } else {
1653 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1654 SCF_TYPE_TIME, &tv, NULL, 0);
1655 }
1656
1657 if (r != 0) {
1658 /*
1659 * There's something amiss with our service
1660 * so we'll print a '-' for STIME.
1661 */
1662 (void) snprintf(newbuf, newsize, "%s%-*s", *buf ? *buf : "",
1663 STIME_COLUMN_WIDTH + 1, "-");
1664 } else {
1665 /* tv should be valid so we'll format it */
1666 then = (time_t)tv.tv_sec;
1667
1668 tm = localtime(&then);
1669 /*
1670 * Print time if started within the past 24 hours, print date
1671 * if within the past 12 months or, finally, print year if
1672 * started greater than 12 months ago.
1673 */
1674 if (now - then < 24 * 60 * 60) {
1675 (void) strftime(st_buf, sizeof (st_buf),
1676 gettext(FORMAT_TIME), tm);
1677 } else if (now - then < 12 * 30 * 24 * 60 * 60) {
1678 (void) strftime(st_buf, sizeof (st_buf),
1679 gettext(FORMAT_DATE), tm);
1680 } else {
1681 (void) strftime(st_buf, sizeof (st_buf),
1682 gettext(FORMAT_YEAR), tm);
1683 }
1684 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1685 STIME_COLUMN_WIDTH + 1, st_buf);
1686 }
1687 if (*buf)
1688 free(*buf);
1689 *buf = newbuf;
1690 }
1691
1692 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t))
1693
1694 /* ARGSUSED */
1695 static void
1696 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
1697 {
1698 struct timeval tv;
1699 int r;
1700
1701 if (wip->pg == NULL)
1702 r = get_restarter_time_prop(wip->inst,
1703 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1704 else
1705 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1706 SCF_TYPE_TIME, &tv, NULL, 0);
1707
1708 if (r == 0) {
1709 int64_t sec;
1714 us = tv.tv_usec;
1715
1716 sec = BE_64(sec);
1717 us = BE_32(us);
1718 bcopy(&sec, buf, sizeof (sec));
1719 bcopy(&us, buf + sizeof (sec), sizeof (us));
1720 } else {
1721 bzero(buf, STIME_SORTKEY_WIDTH);
1722 }
1723
1724 if (reverse)
1725 reverse_bytes(buf, STIME_SORTKEY_WIDTH);
1726 }
1727
1728 /* ZONE */
1729 #define ZONE_COLUMN_WIDTH 16
1730 /*ARGSUSED*/
1731 static void
1732 sprint_zone(char **buf, scf_walkinfo_t *wip)
1733 {
1734 size_t newsize;
1735 char *newbuf, *zonename = g_zonename, b[ZONENAME_MAX];
1736
1737 if (zonename == NULL) {
1738 zoneid_t zoneid = getzoneid();
1739
1740 if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
1741 uu_die(gettext("could not determine zone name"));
1742
1743 zonename = b;
1744 }
1745
1746 if (strlen(zonename) > ZONE_COLUMN_WIDTH)
1747 newsize = (*buf ? strlen(*buf) : 0) + strlen(zonename) + 2;
1748 else
1749 newsize = (*buf ? strlen(*buf) : 0) + ZONE_COLUMN_WIDTH + 2;
1750
1751 newbuf = safe_malloc(newsize);
1752 (void) snprintf(newbuf, newsize, "%s%-*s ", *buf ? *buf : "",
1753 ZONE_COLUMN_WIDTH, zonename);
1754
1755 if (*buf)
1756 free(*buf);
1757 *buf = newbuf;
1758 }
1759
1760 static void
1761 sortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
1762 {
1763 char *tmp = NULL;
1764
1765 sprint_zone(&tmp, wip);
1766 bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
1767 free(tmp);
1768 if (reverse)
1769 reverse_bytes(buf, ZONE_COLUMN_WIDTH);
1770 }
1771
1772 /*
1773 * Information about columns which can be displayed. If you add something,
1774 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1775 */
1776 static const struct column columns[] = {
1777 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
1778 CTID_SORTKEY_WIDTH, sortkey_ctid },
1779 { "DESC", DESC_COLUMN_WIDTH, sprint_desc,
1780 DESC_COLUMN_WIDTH, sortkey_desc },
1781 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
1782 FMRI_COLUMN_WIDTH, sortkey_fmri },
1783 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
1784 COMPONENT_COLUMN_WIDTH, sortkey_instance },
1785 { "N", 1, sprint_n, 1, sortkey_nstate },
1786 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
1787 { "NSTATE", MAX_SCF_STATE_STRING_SZ - 1, sprint_nstate,
1788 1, sortkey_nstate },
1789 { "S", 2, sprint_s, 1, sortkey_state },
1790 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
1791 COMPONENT_COLUMN_WIDTH, sortkey_scope },
1792 { "SN", 2, sprint_sn, 2, sortkey_sn },
1793 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
1794 COMPONENT_COLUMN_WIDTH, sortkey_service },
1795 { "STA", 4, sprint_sta, 1, sortkey_state },
1796 { "STATE", MAX_SCF_STATE_STRING_SZ - 1 + 1, sprint_state,
1797 1, sortkey_state },
1798 { "STIME", STIME_COLUMN_WIDTH, sprint_stime,
1799 STIME_SORTKEY_WIDTH, sortkey_stime },
1800 { "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
1801 ZONE_COLUMN_WIDTH, sortkey_zone },
1802 };
1803
1804 #define MAX_COLUMN_NAME_LENGTH_STR "6"
1805
1806 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
1807
1808 /*
1809 * Necessary thanks to gettext() & xgettext.
1810 */
1811 static const char *
1812 description_of_column(int c)
1813 {
1814 const char *s = NULL;
1815
1816 switch (c) {
1852 break;
1853 case 12:
1854 s = gettext("name for current state");
1855 break;
1856 case 13:
1857 s = gettext("time of last state change");
1858 break;
1859 case 14:
1860 s = gettext("name of zone");
1861 break;
1862 }
1863
1864 assert(s != NULL);
1865 return (s);
1866 }
1867
1868
1869 static void
1870 print_usage(const char *progname, FILE *f, boolean_t do_exit)
1871 {
1872 (void) fprintf(f, gettext(
1873 "Usage: %1$s [-aHpv] [-o col[,col ... ]] [-R restarter] "
1874 "[-sS col] [-Z | -z zone ]\n [<service> ...]\n"
1875 " %1$s -d | -D [-Hpv] [-o col[,col ... ]] [-sS col] "
1876 "[-Z | -z zone ]\n [<service> ...]\n"
1877 " %1$s [-l | -L] [-Z | -z zone] <service> ...\n"
1878 " %1$s -x [-v] [-Z | -z zone] [<service> ...]\n"
1879 " %1$s -?\n"), progname);
1880
1881 if (do_exit)
1882 exit(UU_EXIT_USAGE);
1883 }
1884
1885 #define argserr(progname) print_usage(progname, stderr, B_TRUE)
1886
1887 static void
1888 print_help(const char *progname)
1889 {
1890 int i;
1891
1892 print_usage(progname, stdout, B_FALSE);
1893
1894 (void) printf(gettext("\n"
1895 "\t-a list all service instances rather than "
1896 "only those that are enabled\n"
1897 "\t-d list dependencies of the specified service(s)\n"
1898 "\t-D list dependents of the specified service(s)\n"
1899 "\t-H omit header line from output\n"
3437 /* ARGSUSED */
3438 static void
3439 errignore(const char *str, ...)
3440 {}
3441
3442 int
3443 main(int argc, char **argv)
3444 {
3445 char opt, opt_mode;
3446 int i, n;
3447 char *columns_str = NULL;
3448 char *cp;
3449 const char *progname;
3450 int err, missing = 1, ignored, *errarg;
3451 uint_t nzents = 0, zent = 0;
3452 zoneid_t *zids = NULL;
3453 char zonename[ZONENAME_MAX];
3454 void (*errfunc)(const char *, ...);
3455
3456 int show_all = 0;
3457 int show_header = 1;
3458 int show_zones = 0;
3459
3460 const char * const options = "aHpvno:R:s:S:dDlL?xZz:";
3461
3462 (void) setlocale(LC_ALL, "");
3463
3464 locale = setlocale(LC_MESSAGES, NULL);
3465 if (locale) {
3466 locale = safe_strdup(locale);
3467 _scf_sanitize_locale(locale);
3468 }
3469
3470 (void) textdomain(TEXT_DOMAIN);
3471 progname = uu_setpname(argv[0]);
3472
3473 exit_status = UU_EXIT_OK;
3474
3475 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
3476 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3477 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
3527
3528 default:
3529 break;
3530 }
3531 }
3532
3533 sortkey_sz = 0;
3534
3535 optind = 1; /* Reset getopt() */
3536 while ((opt = getopt(argc, argv, options)) != -1) {
3537 switch (opt) {
3538 case 'a':
3539 if (opt_mode != 0)
3540 argserr(progname);
3541 show_all = 1;
3542 break;
3543
3544 case 'H':
3545 if (opt_mode == 'l' || opt_mode == 'x')
3546 argserr(progname);
3547 show_header = 0;
3548 break;
3549
3550 case 'p':
3551 if (opt_mode == 'x')
3552 argserr(progname);
3553 opt_processes = 1;
3554 break;
3555
3556 case 'v':
3557 opt_verbose = 1;
3558 break;
3559
3560 case 'o':
3561 if (opt_mode == 'l' || opt_mode == 'x')
3562 argserr(progname);
3563 columns_str = optarg;
3564 break;
3565
3566 case 'R':
3567 if (opt_mode != 0 || opt_mode == 'x')
3906 case 'n':
3907 break;
3908
3909 default:
3910 assert(0);
3911 abort();
3912 }
3913
3914 nextzone:
3915 if (show_zones && zent < nzents && exit_status == 0) {
3916 scf_handle_destroy(h);
3917 goto again;
3918 }
3919
3920 if (show_zones && exit_status == 0)
3921 exit_status = missing;
3922
3923 if (opt_columns == NULL)
3924 return (exit_status);
3925
3926 if (show_header)
3927 print_header();
3928
3929 (void) uu_avl_walk(lines, print_line, NULL, 0);
3930
3931 return (exit_status);
3932 }
|
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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011, Joyent, Inc. All rights reserved.
25 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
26 * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
27 */
28
29 /*
30 * svcs - display attributes of service instances
31 *
32 * We have two output formats and six instance selection mechanisms. The
33 * primary output format is a line of attributes (selected by -o), possibly
34 * followed by process description lines (if -p is specified), for each
35 * instance selected. The columns available to display are described by the
36 * struct column columns array. The columns to actually display are kept in
37 * the opt_columns array as indicies into the columns array. The selection
38 * mechanisms available for this format are service FMRIs (selects all child
39 * instances), instance FMRIs, instance FMRI glob patterns, instances with
40 * a certain restarter (-R), dependencies of instances (-d), and dependents of
41 * instances (-D). Since the lines must be sorted (per -sS), we'll just stick
42 * each into a data structure and print them in order when we're done. To
43 * avoid listing the same instance twice (when -d and -D aren't given), we'll
44 * use a hash table of FMRIs to record that we've listed (added to the tree)
45 * an instance.
130 ssize_t max_scf_value_length;
131 ssize_t max_scf_fmri_length;
132 static ssize_t max_scf_type_length;
133 static time_t now;
134 static struct pfmri_list *restarters = NULL;
135 static int first_paragraph = 1; /* For -l mode. */
136 static char *common_name_buf; /* Sized for maximal length value. */
137 char *locale; /* Current locale. */
138 char *g_zonename; /* zone being operated upon */
139
140 /*
141 * Pathname storage for path generated from the fmri.
142 * Used for reading the ctid and (start) pid files for an inetd service.
143 */
144 static char genfmri_filename[MAXPATHLEN] = "";
145
146 /* Options */
147 static int *opt_columns = NULL; /* Indices into columns to display. */
148 static int opt_cnum = 0;
149 static int opt_processes = 0; /* Print processes? */
150 static int opt_scripted = 0; /* No header, tabs as separators. */
151 static int *opt_sort = NULL; /* Indices into columns to sort. */
152 static int opt_snum = 0;
153 static int opt_nstate_shown = 0; /* Will nstate be shown? */
154 static int opt_verbose = 0;
155 static char *opt_zone; /* zone selected, if any */
156
157 /* Minimize string constants. */
158 static const char * const scf_property_state = SCF_PROPERTY_STATE;
159 static const char * const scf_property_next_state = SCF_PROPERTY_NEXT_STATE;
160 static const char * const scf_property_contract = SCF_PROPERTY_CONTRACT;
161
162
163 /*
164 * Utility functions
165 */
166
167 /*
168 * For unexpected libscf errors. The ending newline is necessary to keep
169 * uu_die() from appending the errno error.
170 */
986
987 /*
988 * This function should write sortkey_width bytes into buf which will
989 * cause memcmp() to sort it properly. (Unlike sprint() above,
990 * however, an extra null byte may overrun the buffer.) The second
991 * argument controls whether the results are sorted in forward or
992 * reverse order.
993 */
994 void (*get_sortkey)(char *, int, scf_walkinfo_t *);
995 };
996
997 static void
998 reverse_bytes(char *buf, size_t len)
999 {
1000 int i;
1001
1002 for (i = 0; i < len; ++i)
1003 buf[i] = ~buf[i];
1004 }
1005
1006 static void
1007 sprint_str(char **buf, const char *str, size_t width)
1008 {
1009 char *newbuf;
1010 size_t newsz = (*buf != NULL ? strlen(*buf) : 0) + 2;
1011
1012 if (opt_scripted)
1013 newsz += strlen(str);
1014 else
1015 newsz += width;
1016
1017 newbuf = safe_malloc(newsz);
1018
1019 if (opt_scripted) {
1020 (void) snprintf(newbuf, newsz, "%s%s%s",
1021 *buf != NULL ? *buf : "",
1022 *buf != NULL ? "\t" : "",
1023 str);
1024 } else {
1025 (void) snprintf(newbuf, newsz, "%s%-*s ",
1026 *buf != NULL ? *buf : "", width, str);
1027 }
1028
1029 free(*buf);
1030 *buf = newbuf;
1031 }
1032
1033 /* CTID */
1034 #define CTID_COLUMN_WIDTH 6
1035 #define CTID_COLUMN_BUFSIZE 20 /* max ctid_t + space + \0 */
1036
1037 static void
1038 sprint_ctid(char **buf, scf_walkinfo_t *wip)
1039 {
1040 int r;
1041 uint64_t c;
1042 char ctid_buf[CTID_COLUMN_BUFSIZE] = { 0 };
1043 char *cstr;
1044 int restarter_spec;
1045
1046 /*
1047 * Use the restarter specific get pids routine, if available.
1048 * Only check for non-legacy services (wip->pg == 0).
1049 */
1050 if (wip->pg != NULL) {
1051 r = pg_get_single_val(wip->pg, scf_property_contract,
1052 SCF_TYPE_COUNT, &c, 0, EMPTY_OK | MULTI_OK);
1053 } else {
1054 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1055 NULL, NULL);
1056 if (restarter_spec == 0) {
1057 /* No restarter specific routine */
1058 r = get_restarter_count_prop(wip->inst,
1059 scf_property_contract, &c, EMPTY_OK | MULTI_OK);
1060 }
1061 }
1062
1063 if (r == 0 || r == E2BIG) {
1064 if (r == E2BIG)
1065 ctid_buf[CTID_COLUMN_BUFSIZE - 2] = '*';
1066 cstr = ulltostr(c, &ctid_buf[CTID_COLUMN_BUFSIZE - 2]);
1067 sprint_str(buf, cstr, CTID_COLUMN_WIDTH);
1068 } else {
1069 sprint_str(buf, "-", CTID_COLUMN_WIDTH);
1070 }
1071 }
1072
1073 #define CTID_SORTKEY_WIDTH (sizeof (uint64_t))
1074
1075 static void
1076 sortkey_ctid(char *buf, int reverse, scf_walkinfo_t *wip)
1077 {
1078 int r;
1079 uint64_t c;
1080 int restarter_spec;
1081
1082 /*
1083 * Use the restarter specific get pids routine, if available.
1084 * Only check for non-legacy services (wip->pg == 0).
1085 */
1086 if (wip->pg != NULL) {
1087 r = pg_get_single_val(wip->pg, scf_property_contract,
1088 SCF_TYPE_COUNT, &c, 0, EMPTY_OK);
1089 } else {
1090 r = ctids_by_restarter(wip, &c, 0, MULTI_OK, &restarter_spec,
1102 * work.
1103 */
1104 c = BE_64(c);
1105
1106 bcopy(&c, buf, CTID_SORTKEY_WIDTH);
1107 } else {
1108 bzero(buf, CTID_SORTKEY_WIDTH);
1109 }
1110
1111 if (reverse)
1112 reverse_bytes(buf, CTID_SORTKEY_WIDTH);
1113 }
1114
1115 /* DESC */
1116 #define DESC_COLUMN_WIDTH 100
1117
1118 static void
1119 sprint_desc(char **buf, scf_walkinfo_t *wip)
1120 {
1121 char *x;
1122
1123 if (common_name_buf == NULL)
1124 common_name_buf = safe_malloc(max_scf_value_length + 1);
1125
1126 bzero(common_name_buf, max_scf_value_length + 1);
1127
1128 if (wip->pg != NULL) {
1129 common_name_buf[0] = '-';
1130 } else if (inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, locale,
1131 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1132 1, 1) == -1 &&
1133 inst_get_single_val(wip->inst, SCF_PG_TM_COMMON_NAME, "C",
1134 SCF_TYPE_USTRING, common_name_buf, max_scf_value_length, 0,
1135 1, 1) == -1) {
1136 common_name_buf[0] = '-';
1137 }
1138
1139 /*
1140 * Collapse multi-line tm_common_name values into a single line.
1141 */
1142 for (x = common_name_buf; *x != '\0'; x++)
1143 if (*x == '\n')
1144 *x = ' ';
1145
1146 sprint_str(buf, common_name_buf, DESC_COLUMN_WIDTH);
1147 }
1148
1149 /* ARGSUSED */
1150 static void
1151 sortkey_desc(char *buf, int reverse, scf_walkinfo_t *wip)
1152 {
1153 bzero(buf, DESC_COLUMN_WIDTH);
1154 }
1155
1156 /* State columns (STATE, NSTATE, S, N, SN, STA, NSTA) */
1157
1158 static char
1159 state_to_char(const char *state)
1160 {
1161 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1162 return ('u');
1163
1164 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1165 return ('0');
1166
1216 *buf = 4;
1217 else if (strcmp(state_name, SCF_STATE_STRING_MAINT) == 0)
1218 *buf = 5;
1219 else if (strcmp(state_name, SCF_STATE_STRING_DISABLED) == 0)
1220 *buf = 1;
1221 else if (strcmp(state_name, SCF_STATE_STRING_UNINIT) == 0)
1222 *buf = 6;
1223 else
1224 *buf = 7;
1225 } else
1226 *buf = 0;
1227
1228 if (reverse)
1229 *buf = 255 - *buf;
1230 }
1231
1232 static void
1233 sprint_state(char **buf, scf_walkinfo_t *wip)
1234 {
1235 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1236
1237 if (wip->pg == NULL) {
1238 get_restarter_string_prop(wip->inst, scf_property_state,
1239 state_name, sizeof (state_name));
1240
1241 /* Don't print blank fields, to ease parsing. */
1242 if (state_name[0] == '\0') {
1243 state_name[0] = '-';
1244 state_name[1] = '\0';
1245 }
1246
1247 if (!opt_nstate_shown && transitioning(wip->inst)) {
1248 /* Append an asterisk if nstate is valid. */
1249 (void) strcat(state_name, "*");
1250 }
1251 } else
1252 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1253
1254 sprint_str(buf, state_name, MAX_SCF_STATE_STRING_SZ);
1255 }
1256
1257 static void
1258 sortkey_state(char *buf, int reverse, scf_walkinfo_t *wip)
1259 {
1260 sortkey_states(scf_property_state, buf, reverse, wip);
1261 }
1262
1263 static void
1264 sprint_nstate(char **buf, scf_walkinfo_t *wip)
1265 {
1266 char next_state_name[MAX_SCF_STATE_STRING_SZ + 1];
1267 boolean_t blank = 0;
1268
1269 if (wip->pg == NULL) {
1270 get_restarter_string_prop(wip->inst, scf_property_next_state,
1271 next_state_name, sizeof (next_state_name));
1272
1273 /* Don't print blank fields, to ease parsing. */
1274 if (next_state_name[0] == '\0' ||
1275 strcmp(next_state_name, SCF_STATE_STRING_NONE) == 0)
1276 blank = 1;
1277 } else {
1278 blank = 1;
1279 }
1280
1281 if (blank) {
1282 next_state_name[0] = '-';
1283 next_state_name[1] = '\0';
1284 }
1285
1286 sprint_str(buf, next_state_name, MAX_SCF_STATE_STRING_SZ);
1287 }
1288
1289 static void
1290 sortkey_nstate(char *buf, int reverse, scf_walkinfo_t *wip)
1291 {
1292 sortkey_states(scf_property_next_state, buf, reverse, wip);
1293 }
1294
1295 static void
1296 sprint_s(char **buf, scf_walkinfo_t *wip)
1297 {
1298 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1299 char tmp[3] = { 0 };
1300
1301 if (wip->pg == NULL) {
1302 get_restarter_string_prop(wip->inst, scf_property_state,
1303 state_name, sizeof (state_name));
1304 tmp[0] = state_to_char(state_name);
1305
1306 if (!opt_nstate_shown && transitioning(wip->inst))
1307 tmp[1] = '*';
1308 else
1309 tmp[1] = ' ';
1310 } else {
1311 tmp[0] = 'L';
1312 tmp[1] = ' ';
1313 }
1314 tmp[2] = ' ';
1315
1316 sprint_str(buf, tmp, 2);
1317 }
1318
1319 static void
1320 sprint_n(char **buf, scf_walkinfo_t *wip)
1321 {
1322 char nstate_name[MAX_SCF_STATE_STRING_SZ + 1];
1323 char tmp[2] = { 0 };
1324
1325 if (wip->pg == NULL) {
1326 get_restarter_string_prop(wip->inst, scf_property_next_state,
1327 nstate_name, sizeof (nstate_name));
1328
1329 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1330 tmp[0] = '-';
1331 else
1332 tmp[0] = state_to_char(nstate_name);
1333 } else {
1334 tmp[0] = '-';
1335 }
1336
1337 sprint_str(buf, tmp, 1);
1338 }
1339
1340 static void
1341 sprint_sn(char **buf, scf_walkinfo_t *wip)
1342 {
1343 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1344 char nstate_name[MAX_SCF_STATE_STRING_SZ + 1];
1345 char tmp[3] = { 0 };
1346
1347 if (wip->pg == NULL) {
1348 get_restarter_string_prop(wip->inst, scf_property_state,
1349 state_name, sizeof (state_name));
1350 get_restarter_string_prop(wip->inst, scf_property_next_state,
1351 nstate_name, sizeof (nstate_name));
1352 tmp[0] = state_to_char(state_name);
1353
1354 if (strcmp(nstate_name, SCF_STATE_STRING_NONE) == 0)
1355 tmp[1] = '-';
1356 else
1357 tmp[1] = state_to_char(nstate_name);
1358 } else {
1359 tmp[0] = 'L';
1360 tmp[1] = '-';
1361 }
1362
1363 sprint_str(buf, tmp, 2);
1364 }
1365
1366 /* ARGSUSED */
1367 static void
1368 sortkey_sn(char *buf, int reverse, scf_walkinfo_t *wip)
1369 {
1370 sortkey_state(buf, reverse, wip);
1371 sortkey_nstate(buf + 1, reverse, wip);
1372 }
1373
1374 static const char *
1375 state_abbrev(const char *state)
1376 {
1377 if (strcmp(state, SCF_STATE_STRING_UNINIT) == 0)
1378 return ("UN");
1379 if (strcmp(state, SCF_STATE_STRING_OFFLINE) == 0)
1380 return ("OFF");
1381 if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0)
1382 return ("ON");
1383 if (strcmp(state, SCF_STATE_STRING_MAINT) == 0)
1384 return ("MNT");
1385 if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0)
1386 return ("DIS");
1387 if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0)
1388 return ("DGD");
1389 if (strcmp(state, SCF_STATE_STRING_LEGACY) == 0)
1390 return ("LRC");
1391
1392 return ("?");
1393 }
1394
1395 static void
1396 sprint_sta(char **buf, scf_walkinfo_t *wip)
1397 {
1398 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1399 char sta[5] = { 0 };
1400
1401 if (wip->pg == NULL) {
1402 get_restarter_string_prop(wip->inst, scf_property_state,
1403 state_name, sizeof (state_name));
1404 } else {
1405 (void) strcpy(state_name, SCF_STATE_STRING_LEGACY);
1406 }
1407
1408 (void) strcpy(sta, state_abbrev(state_name));
1409
1410 if (wip->pg == NULL && !opt_nstate_shown && transitioning(wip->inst))
1411 (void) strcat(sta, "*");
1412
1413 sprint_str(buf, sta, 4);
1414 }
1415
1416 static void
1417 sprint_nsta(char **buf, scf_walkinfo_t *wip)
1418 {
1419 char state_name[MAX_SCF_STATE_STRING_SZ + 1];
1420
1421 if (wip->pg == NULL) {
1422 get_restarter_string_prop(wip->inst, scf_property_next_state,
1423 state_name, sizeof (state_name));
1424 } else {
1425 (void) strcpy(state_name, SCF_STATE_STRING_NONE);
1426 }
1427
1428 if (strcmp(state_name, SCF_STATE_STRING_NONE) == 0)
1429 sprint_str(buf, "-", 4);
1430 else
1431 sprint_str(buf, state_abbrev(state_name), 4);
1432 }
1433
1434 /* FMRI */
1435 #define FMRI_COLUMN_WIDTH 50
1436 static void
1437 sprint_fmri(char **buf, scf_walkinfo_t *wip)
1438 {
1439 char *fmri_buf = safe_malloc(max_scf_fmri_length + 1);
1440
1441 if (wip->pg == NULL) {
1442 if (scf_instance_to_fmri(wip->inst, fmri_buf,
1443 max_scf_fmri_length + 1) == -1)
1444 scfdie();
1445 } else {
1446 (void) strcpy(fmri_buf, SCF_FMRI_LEGACY_PREFIX);
1447 if (pg_get_single_val(wip->pg, SCF_LEGACY_PROPERTY_NAME,
1448 SCF_TYPE_ASTRING, fmri_buf +
1449 sizeof (SCF_FMRI_LEGACY_PREFIX) - 1,
1450 max_scf_fmri_length + 1 -
1451 (sizeof (SCF_FMRI_LEGACY_PREFIX) - 1), 0) != 0)
1452 (void) strcat(fmri_buf, LEGACY_UNKNOWN);
1453 }
1454
1455 sprint_str(buf, fmri_buf, FMRI_COLUMN_WIDTH);
1456 free(fmri_buf);
1457 }
1458
1459 static void
1460 sortkey_fmri(char *buf, int reverse, scf_walkinfo_t *wip)
1461 {
1462 char *tmp = NULL;
1463
1464 sprint_fmri(&tmp, wip);
1465 bcopy(tmp, buf, FMRI_COLUMN_WIDTH);
1466 free(tmp);
1467 if (reverse)
1468 reverse_bytes(buf, FMRI_COLUMN_WIDTH);
1469 }
1470
1471 /* Component columns */
1472 #define COMPONENT_COLUMN_WIDTH 20
1473 static void
1474 sprint_scope(char **buf, scf_walkinfo_t *wip)
1475 {
1476 char *scope_buf = safe_malloc(max_scf_name_length + 1);
1477
1478 assert(wip->scope != NULL);
1479
1480 if (scf_scope_get_name(wip->scope, scope_buf, max_scf_name_length) < 0)
1481 scfdie();
1482
1483 sprint_str(buf, scope_buf, COMPONENT_COLUMN_WIDTH);
1484 free(scope_buf);
1485 }
1486
1487 static void
1488 sortkey_scope(char *buf, int reverse, scf_walkinfo_t *wip)
1489 {
1490 char *tmp = NULL;
1491
1492 sprint_scope(&tmp, wip);
1493 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1494 free(tmp);
1495 if (reverse)
1496 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1497 }
1498
1499 static void
1500 sprint_service(char **buf, scf_walkinfo_t *wip)
1501 {
1502 char *svc_buf = safe_malloc(max_scf_name_length + 1);
1503
1504 if (wip->pg == NULL) {
1505 if (scf_service_get_name(wip->svc, svc_buf,
1506 max_scf_name_length + 1) < 0)
1507 scfdie();
1508 } else {
1509 if (pg_get_single_val(wip->pg, "name", SCF_TYPE_ASTRING,
1510 svc_buf, max_scf_name_length + 1, EMPTY_OK) != 0)
1511 (void) strcpy(svc_buf, LEGACY_UNKNOWN);
1512 }
1513
1514 sprint_str(buf, svc_buf, COMPONENT_COLUMN_WIDTH);
1515 free(svc_buf);
1516 }
1517
1518 static void
1519 sortkey_service(char *buf, int reverse, scf_walkinfo_t *wip)
1520 {
1521 char *tmp = NULL;
1522
1523 sprint_service(&tmp, wip);
1524 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1525 free(tmp);
1526 if (reverse)
1527 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1528 }
1529
1530 /* INST */
1531 static void
1532 sprint_instance(char **buf, scf_walkinfo_t *wip)
1533 {
1534 char *inst_buf = safe_malloc(max_scf_name_length + 1);
1535
1536 if (wip->pg == NULL) {
1537 if (scf_instance_get_name(wip->inst, inst_buf,
1538 max_scf_name_length + 1) < 0)
1539 scfdie();
1540 } else {
1541 inst_buf[0] = '-';
1542 inst_buf[1] = '\0';
1543 }
1544
1545
1546 sprint_str(buf, inst_buf, COMPONENT_COLUMN_WIDTH);
1547 free(inst_buf);
1548 }
1549
1550 static void
1551 sortkey_instance(char *buf, int reverse, scf_walkinfo_t *wip)
1552 {
1553 char *tmp = NULL;
1554
1555 sprint_instance(&tmp, wip);
1556 bcopy(tmp, buf, COMPONENT_COLUMN_WIDTH);
1557 free(tmp);
1558 if (reverse)
1559 reverse_bytes(buf, COMPONENT_COLUMN_WIDTH);
1560 }
1561
1562 /* STIME */
1563 #define STIME_COLUMN_WIDTH 8
1564 #define FORMAT_TIME "%k:%M:%S"
1565 #define FORMAT_DATE "%b_%d"
1566 #define FORMAT_YEAR "%Y"
1567
1568 /*
1569 * sprint_stime() will allocate a new buffer and snprintf the services's
1570 * state timestamp. If the timestamp is unavailable for some reason
1571 * a '-' is given instead.
1572 */
1573 static void
1574 sprint_stime(char **buf, scf_walkinfo_t *wip)
1575 {
1576 int r;
1577 struct timeval tv;
1578 time_t then;
1579 struct tm *tm;
1580 char st_buf[STIME_COLUMN_WIDTH + 1];
1581
1582 if (wip->pg == NULL) {
1583 r = get_restarter_time_prop(wip->inst,
1584 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1585 } else {
1586 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1587 SCF_TYPE_TIME, &tv, NULL, 0);
1588 }
1589
1590 if (r != 0) {
1591 /*
1592 * There's something amiss with our service
1593 * so we'll print a '-' for STIME.
1594 */
1595 sprint_str(buf, "-", STIME_COLUMN_WIDTH);
1596 } else {
1597 /* tv should be valid so we'll format it */
1598 then = (time_t)tv.tv_sec;
1599
1600 tm = localtime(&then);
1601 /*
1602 * Print time if started within the past 24 hours, print date
1603 * if within the past 12 months or, finally, print year if
1604 * started greater than 12 months ago.
1605 */
1606 if (now - then < 24 * 60 * 60) {
1607 (void) strftime(st_buf, sizeof (st_buf),
1608 gettext(FORMAT_TIME), tm);
1609 } else if (now - then < 12 * 30 * 24 * 60 * 60) {
1610 (void) strftime(st_buf, sizeof (st_buf),
1611 gettext(FORMAT_DATE), tm);
1612 } else {
1613 (void) strftime(st_buf, sizeof (st_buf),
1614 gettext(FORMAT_YEAR), tm);
1615 }
1616 sprint_str(buf, st_buf, STIME_COLUMN_WIDTH);
1617 }
1618 }
1619
1620 #define STIME_SORTKEY_WIDTH (sizeof (uint64_t) + sizeof (uint32_t))
1621
1622 /* ARGSUSED */
1623 static void
1624 sortkey_stime(char *buf, int reverse, scf_walkinfo_t *wip)
1625 {
1626 struct timeval tv;
1627 int r;
1628
1629 if (wip->pg == NULL)
1630 r = get_restarter_time_prop(wip->inst,
1631 SCF_PROPERTY_STATE_TIMESTAMP, &tv, 0);
1632 else
1633 r = pg_get_single_val(wip->pg, SCF_PROPERTY_STATE_TIMESTAMP,
1634 SCF_TYPE_TIME, &tv, NULL, 0);
1635
1636 if (r == 0) {
1637 int64_t sec;
1642 us = tv.tv_usec;
1643
1644 sec = BE_64(sec);
1645 us = BE_32(us);
1646 bcopy(&sec, buf, sizeof (sec));
1647 bcopy(&us, buf + sizeof (sec), sizeof (us));
1648 } else {
1649 bzero(buf, STIME_SORTKEY_WIDTH);
1650 }
1651
1652 if (reverse)
1653 reverse_bytes(buf, STIME_SORTKEY_WIDTH);
1654 }
1655
1656 /* ZONE */
1657 #define ZONE_COLUMN_WIDTH 16
1658 /*ARGSUSED*/
1659 static void
1660 sprint_zone(char **buf, scf_walkinfo_t *wip)
1661 {
1662 char *zonename = g_zonename, b[ZONENAME_MAX];
1663
1664 if (zonename == NULL) {
1665 zoneid_t zoneid = getzoneid();
1666
1667 if (getzonenamebyid(zoneid, b, sizeof (b)) < 0)
1668 uu_die(gettext("could not determine zone name"));
1669
1670 zonename = b;
1671 }
1672
1673 sprint_str(buf, zonename, ZONE_COLUMN_WIDTH);
1674 }
1675
1676 static void
1677 sortkey_zone(char *buf, int reverse, scf_walkinfo_t *wip)
1678 {
1679 char *tmp = NULL;
1680
1681 sprint_zone(&tmp, wip);
1682 bcopy(tmp, buf, ZONE_COLUMN_WIDTH);
1683 free(tmp);
1684 if (reverse)
1685 reverse_bytes(buf, ZONE_COLUMN_WIDTH);
1686 }
1687
1688 /*
1689 * Information about columns which can be displayed. If you add something,
1690 * check MAX_COLUMN_NAME_LENGTH_STR & update description_of_column() below.
1691 */
1692 static const struct column columns[] = {
1693 { "CTID", CTID_COLUMN_WIDTH, sprint_ctid,
1694 CTID_SORTKEY_WIDTH, sortkey_ctid },
1695 { "DESC", DESC_COLUMN_WIDTH, sprint_desc,
1696 DESC_COLUMN_WIDTH, sortkey_desc },
1697 { "FMRI", FMRI_COLUMN_WIDTH, sprint_fmri,
1698 FMRI_COLUMN_WIDTH, sortkey_fmri },
1699 { "INST", COMPONENT_COLUMN_WIDTH, sprint_instance,
1700 COMPONENT_COLUMN_WIDTH, sortkey_instance },
1701 { "N", 1, sprint_n, 1, sortkey_nstate },
1702 { "NSTA", 4, sprint_nsta, 1, sortkey_nstate },
1703 { "NSTATE", MAX_SCF_STATE_STRING_SZ, sprint_nstate,
1704 1, sortkey_nstate },
1705 { "S", 2, sprint_s, 1, sortkey_state },
1706 { "SCOPE", COMPONENT_COLUMN_WIDTH, sprint_scope,
1707 COMPONENT_COLUMN_WIDTH, sortkey_scope },
1708 { "SN", 2, sprint_sn, 2, sortkey_sn },
1709 { "SVC", COMPONENT_COLUMN_WIDTH, sprint_service,
1710 COMPONENT_COLUMN_WIDTH, sortkey_service },
1711 { "STA", 4, sprint_sta, 1, sortkey_state },
1712 { "STATE", MAX_SCF_STATE_STRING_SZ, sprint_state,
1713 1, sortkey_state },
1714 { "STIME", STIME_COLUMN_WIDTH, sprint_stime,
1715 STIME_SORTKEY_WIDTH, sortkey_stime },
1716 { "ZONE", ZONE_COLUMN_WIDTH, sprint_zone,
1717 ZONE_COLUMN_WIDTH, sortkey_zone },
1718 };
1719
1720 #define MAX_COLUMN_NAME_LENGTH_STR "6"
1721
1722 static const int ncolumns = sizeof (columns) / sizeof (columns[0]);
1723
1724 /*
1725 * Necessary thanks to gettext() & xgettext.
1726 */
1727 static const char *
1728 description_of_column(int c)
1729 {
1730 const char *s = NULL;
1731
1732 switch (c) {
1768 break;
1769 case 12:
1770 s = gettext("name for current state");
1771 break;
1772 case 13:
1773 s = gettext("time of last state change");
1774 break;
1775 case 14:
1776 s = gettext("name of zone");
1777 break;
1778 }
1779
1780 assert(s != NULL);
1781 return (s);
1782 }
1783
1784
1785 static void
1786 print_usage(const char *progname, FILE *f, boolean_t do_exit)
1787 {
1788 (void) fprintf(f, gettext("usage:\n"
1789 "%1$s\t[-aHpv?] [-o col[,col]...] [-R FMRI-instance]... "
1790 "[-sS col]...\n\t[-z zone|-Z] [FMRI|pattern]...\n"
1791 "%1$s\t{-d|-D} -Hpv? [-o col[,col]...] [-sS col]... [-z zone|-Z]\n"
1792 "\t[FMRI|pattern]...\n"
1793 "%1$s\t{-l|-L} [-v] [-z zone|-Z] {FMRI|pattern}...\n"
1794 "%1$s\t-x [-v] [-z zone|-Z] [FMRI]...\n"), progname);
1795
1796 if (do_exit)
1797 exit(UU_EXIT_USAGE);
1798 }
1799
1800 #define argserr(progname) print_usage(progname, stderr, B_TRUE)
1801
1802 static void
1803 print_help(const char *progname)
1804 {
1805 int i;
1806
1807 print_usage(progname, stdout, B_FALSE);
1808
1809 (void) printf(gettext("\n"
1810 "\t-a list all service instances rather than "
1811 "only those that are enabled\n"
1812 "\t-d list dependencies of the specified service(s)\n"
1813 "\t-D list dependents of the specified service(s)\n"
1814 "\t-H omit header line from output\n"
3352 /* ARGSUSED */
3353 static void
3354 errignore(const char *str, ...)
3355 {}
3356
3357 int
3358 main(int argc, char **argv)
3359 {
3360 char opt, opt_mode;
3361 int i, n;
3362 char *columns_str = NULL;
3363 char *cp;
3364 const char *progname;
3365 int err, missing = 1, ignored, *errarg;
3366 uint_t nzents = 0, zent = 0;
3367 zoneid_t *zids = NULL;
3368 char zonename[ZONENAME_MAX];
3369 void (*errfunc)(const char *, ...);
3370
3371 int show_all = 0;
3372 int show_zones = 0;
3373
3374 const char * const options = "aHpvno:R:s:S:dDlL?xZz:";
3375
3376 (void) setlocale(LC_ALL, "");
3377
3378 locale = setlocale(LC_MESSAGES, NULL);
3379 if (locale) {
3380 locale = safe_strdup(locale);
3381 _scf_sanitize_locale(locale);
3382 }
3383
3384 (void) textdomain(TEXT_DOMAIN);
3385 progname = uu_setpname(argv[0]);
3386
3387 exit_status = UU_EXIT_OK;
3388
3389 max_scf_name_length = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
3390 max_scf_value_length = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3391 max_scf_fmri_length = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
3441
3442 default:
3443 break;
3444 }
3445 }
3446
3447 sortkey_sz = 0;
3448
3449 optind = 1; /* Reset getopt() */
3450 while ((opt = getopt(argc, argv, options)) != -1) {
3451 switch (opt) {
3452 case 'a':
3453 if (opt_mode != 0)
3454 argserr(progname);
3455 show_all = 1;
3456 break;
3457
3458 case 'H':
3459 if (opt_mode == 'l' || opt_mode == 'x')
3460 argserr(progname);
3461 opt_scripted = 1;
3462 break;
3463
3464 case 'p':
3465 if (opt_mode == 'x')
3466 argserr(progname);
3467 opt_processes = 1;
3468 break;
3469
3470 case 'v':
3471 opt_verbose = 1;
3472 break;
3473
3474 case 'o':
3475 if (opt_mode == 'l' || opt_mode == 'x')
3476 argserr(progname);
3477 columns_str = optarg;
3478 break;
3479
3480 case 'R':
3481 if (opt_mode != 0 || opt_mode == 'x')
3820 case 'n':
3821 break;
3822
3823 default:
3824 assert(0);
3825 abort();
3826 }
3827
3828 nextzone:
3829 if (show_zones && zent < nzents && exit_status == 0) {
3830 scf_handle_destroy(h);
3831 goto again;
3832 }
3833
3834 if (show_zones && exit_status == 0)
3835 exit_status = missing;
3836
3837 if (opt_columns == NULL)
3838 return (exit_status);
3839
3840 if (!opt_scripted)
3841 print_header();
3842
3843 (void) uu_avl_walk(lines, print_line, NULL, 0);
3844
3845 return (exit_status);
3846 }
|