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 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright (c) 2015 by Delphix. All rights reserved.
  29  * Copyright 2017 Joyent, Inc.
  30  */
  31 
  32 #include <errno.h>
  33 #include <sys/types.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <strings.h>
  37 #include <stdio.h>
  38 #include <ofmt.h>
  39 #include <sys/termios.h>
  40 #include <unistd.h>
  41 #include <sys/sysmacros.h>
  42 #include <libintl.h>
  43 #include <assert.h>
  44 
  45 /*
  46  * functions and structures to internally process a comma-separated string
  47  * of fields selected for output.
  48  */
  49 typedef struct {
  50         char    *s_buf;
  51         const char **s_fields;  /* array of pointers to the fields in s_buf */
  52         uint_t  s_nfields;      /* the number of fields in s_buf */
  53         uint_t  s_currfield;    /* the current field being processed */
  54 } split_t;
  55 
  56 static void splitfree(split_t *);
  57 static split_t *split_str(const char *, uint_t);
  58 static split_t *split_fields(const ofmt_field_t *, uint_t, uint_t);
  59 
  60 /*
  61  * The state of the output is tracked in a ofmt_state_t structure.
  62  * Each os_fields[i] entry points at an ofmt_field_t array for
  63  * the sub-command whose contents are provided by the caller, with
  64  * os_nfields set to the number of requested fields.
  65  */
  66 typedef struct ofmt_state_s {
  67         ofmt_field_t    *os_fields;
  68         uint_t          os_nfields;
  69         boolean_t       os_lastfield;
  70         uint_t          os_overflow;
  71         struct winsize  os_winsize;
  72         int             os_nrow;
  73         uint_t          os_flags;
  74         int             os_nbad;
  75         char            **os_badfields;
  76         int             os_maxnamelen;  /* longest name (f. multiline) */
  77         char            os_fs;          /* field seperator */
  78 } ofmt_state_t;
  79 /*
  80  * A B_TRUE return value from the callback function will print out the contents
  81  * of the output buffer, except when the buffer is returned with the empty
  82  * string "", in which case the  OFMT_VAL_UNDEF will be printed.
  83  *
  84  * If the callback function returns B_FALSE, the "?" string will be emitted.
  85  */
  86 #define OFMT_VAL_UNDEF          "--"
  87 #define OFMT_VAL_UNKNOWN        "?"
  88 
  89 #define OFMT_DEFAULT_FS         ':'
  90 
  91 /*
  92  * The maximum number of rows supported by the OFMT_WRAP option.
  93  */
  94 #define OFMT_MAX_ROWS           128
  95 
  96 static void ofmt_print_field(ofmt_state_t *, ofmt_field_t *, const char *,
  97     boolean_t);
  98 
  99 /*
 100  * Split `str' into at most `maxfields' fields, Return a pointer to a
 101  * split_t containing the split fields, or NULL on failure.
 102  */
 103 static split_t *
 104 split_str(const char *str, uint_t maxfields)
 105 {
 106         char    *field, *token, *lasts = NULL;
 107         split_t *sp;
 108 
 109         if (*str == '\0' || maxfields == 0)
 110                 return (NULL);
 111 
 112         sp = calloc(sizeof (split_t), 1);
 113         if (sp == NULL)
 114                 return (NULL);
 115 
 116         sp->s_buf = strdup(str);
 117         sp->s_fields = malloc(sizeof (char *) * maxfields);
 118         if (sp->s_buf == NULL || sp->s_fields == NULL)
 119                 goto fail;
 120 
 121         token = sp->s_buf;
 122         while ((field = strtok_r(token, ",", &lasts)) != NULL) {
 123                 if (sp->s_nfields == maxfields)
 124                         goto fail;
 125                 token = NULL;
 126                 sp->s_fields[sp->s_nfields++] = field;
 127         }
 128         return (sp);
 129 fail:
 130         splitfree(sp);
 131         return (NULL);
 132 }
 133 
 134 /*
 135  * Split `fields' into at most `maxfields' fields. Return a pointer to
 136  * a split_t containing the split fields, or NULL on failure. Invoked
 137  * when all fields are implicitly selected at handle creation by
 138  * passing in a NULL fields_str
 139  */
 140 static split_t *
 141 split_fields(const ofmt_field_t *template, uint_t maxfields, uint_t maxcols)
 142 {
 143         split_t *sp;
 144         int i, cols;
 145 
 146         sp = calloc(sizeof (split_t), 1);
 147         if (sp == NULL)
 148                 return (NULL);
 149 
 150         sp->s_fields = malloc(sizeof (char *) * maxfields);
 151         if (sp->s_fields == NULL)
 152                 goto fail;
 153         cols = 0;
 154         for (i = 0; i < maxfields; i++) {
 155                 cols += template[i].of_width;
 156                 /*
 157                  * If all fields are implied without explicitly passing
 158                  * in a fields_str, build a list of field names, stopping
 159                  * when we run out of columns.
 160                  */
 161                 if (maxcols > 0 && cols > maxcols)
 162                         break;
 163                 sp->s_fields[sp->s_nfields++] = template[i].of_name;
 164         }
 165         return (sp);
 166 fail:
 167         splitfree(sp);
 168         return (NULL);
 169 }
 170 
 171 /*
 172  * Free the split_t structure pointed to by `sp'.
 173  */
 174 static void
 175 splitfree(split_t *sp)
 176 {
 177         if (sp == NULL)
 178                 return;
 179         free(sp->s_buf);
 180         free(sp->s_fields);
 181         free(sp);
 182 }
 183 
 184 /*
 185  * Open a handle to be used for printing formatted output.
 186  */
 187 ofmt_status_t
 188 ofmt_open(const char *str, const ofmt_field_t *template, uint_t flags,
 189     uint_t maxcols, ofmt_handle_t *ofmt)
 190 {
 191         split_t         *sp;
 192         uint_t          i, j, of_index;
 193         const ofmt_field_t *ofp;
 194         ofmt_field_t    *of;
 195         ofmt_state_t    *os = NULL;
 196         uint_t          nfields = 0;
 197         ofmt_status_t   error = OFMT_SUCCESS;
 198         boolean_t       parsable = (flags & OFMT_PARSABLE);
 199         boolean_t       wrap = (flags & OFMT_WRAP);
 200         boolean_t       multiline = (flags & OFMT_MULTILINE);
 201 
 202         *ofmt = NULL;
 203         if (parsable) {
 204                 if (multiline)
 205                         return (OFMT_EPARSEMULTI);
 206                 /*
 207                  * For parsable output mode, the caller always needs
 208                  * to specify precisely which fields are to be selected,
 209                  * since the set of fields may change over time.
 210                  */
 211                 if (str == NULL || str[0] == '\0')
 212                         return (OFMT_EPARSENONE);
 213                 if (strcasecmp(str, "all") == 0)
 214                         return (OFMT_EPARSEALL);
 215                 if (wrap)
 216                         return (OFMT_EPARSEWRAP);
 217         }
 218         if (template == NULL)
 219                 return (OFMT_ENOTEMPLATE);
 220         for (ofp = template; ofp->of_name != NULL; ofp++)
 221                 nfields++;
 222         /*
 223          * split str into the columns selected, or construct the
 224          * full set of columns (equivalent to -o all).
 225          */
 226         if (str != NULL && strcasecmp(str, "all") != 0) {
 227                 sp = split_str(str, nfields);
 228         } else {
 229                 if (parsable || (str != NULL && strcasecmp(str, "all") == 0))
 230                         maxcols = 0;
 231                 sp = split_fields(template, nfields, maxcols);
 232         }
 233         if (sp == NULL)
 234                 goto nomem;
 235 
 236         os = calloc(sizeof (ofmt_state_t) +
 237             sp->s_nfields * sizeof (ofmt_field_t), 1);
 238         if (os == NULL)
 239                 goto nomem;
 240         *ofmt = os;
 241         os->os_fields = (ofmt_field_t *)&os[1];
 242         os->os_flags = flags;
 243         os->os_fs = OFMT_DEFAULT_FS;
 244 
 245         of = os->os_fields;
 246         of_index = 0;
 247         /*
 248          * sp->s_nfields is the number of fields requested in fields_str.
 249          * nfields is the number of fields in template.
 250          */
 251         for (i = 0; i < sp->s_nfields; i++) {
 252                 for (j = 0; j < nfields; j++) {
 253                         if (strcasecmp(sp->s_fields[i],
 254                             template[j].of_name) == 0) {
 255                                 break;
 256                         }
 257                 }
 258                 if (j == nfields) {
 259                         int nbad = os->os_nbad++;
 260 
 261                         error = OFMT_EBADFIELDS;
 262                         if (os->os_badfields == NULL) {
 263                                 os->os_badfields = malloc(sp->s_nfields *
 264                                     sizeof (char *));
 265                                 if (os->os_badfields == NULL)
 266                                         goto nomem;
 267                         }
 268                         os->os_badfields[nbad] = strdup(sp->s_fields[i]);
 269                         if (os->os_badfields[nbad] == NULL)
 270                                 goto nomem;
 271                         continue;
 272                 }
 273                 of[of_index].of_name = strdup(template[j].of_name);
 274                 if (of[of_index].of_name == NULL)
 275                         goto nomem;
 276                 if (multiline) {
 277                         int n = strlen(of[of_index].of_name);
 278 
 279                         os->os_maxnamelen = MAX(n, os->os_maxnamelen);
 280                 }
 281                 of[of_index].of_width = template[j].of_width;
 282                 of[of_index].of_id = template[j].of_id;
 283                 of[of_index].of_cb = template[j].of_cb;
 284                 of_index++;
 285         }
 286         splitfree(sp);
 287         if (of_index == 0) /* all values in str are bogus */
 288                 return (OFMT_ENOFIELDS);
 289         os->os_nfields = of_index; /* actual number of fields printed */
 290         ofmt_update_winsize(*ofmt);
 291         return (error);
 292 nomem:
 293         error = OFMT_ENOMEM;
 294         if (os != NULL)
 295                 ofmt_close(os);
 296         *ofmt = NULL;
 297         splitfree(sp);
 298         return (error);
 299 }
 300 
 301 void
 302 ofmt_set_fs(ofmt_handle_t ofmt, char fs)
 303 {
 304         ((ofmt_state_t *)ofmt)->os_fs = fs;
 305 }
 306 
 307 /*
 308  * free resources associated with the ofmt_handle_t
 309  */
 310 void
 311 ofmt_close(ofmt_handle_t ofmt)
 312 {
 313         ofmt_state_t *os = ofmt;
 314         int i;
 315 
 316         if (os == NULL)
 317                 return;
 318         for (i = 0; i < os->os_nfields; i++)
 319                 free(os->os_fields[i].of_name);
 320         for (i = 0; i < os->os_nbad; i++)
 321                 free(os->os_badfields[i]);
 322         free(os->os_badfields);
 323         free(os);
 324 }
 325 
 326 /*
 327  * Print the value for the selected field by calling the callback-function
 328  * registered for the field.
 329  */
 330 static void
 331 ofmt_print_field(ofmt_state_t *os, ofmt_field_t *ofp, const char *value,
 332     boolean_t escsep)
 333 {
 334         uint_t  width = ofp->of_width;
 335         uint_t  valwidth;
 336         uint_t  compress;
 337         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 338         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 339         boolean_t rightjust = (os->os_flags & OFMT_RIGHTJUST);
 340         char    c;
 341 
 342         /*
 343          * Parsable fields are separated by os_fs. os_fs and '\' are escaped
 344          * (prefixed by a) '\'.
 345          */
 346         if (parsable) {
 347                 if (os->os_nfields == 1) {
 348                         (void) printf("%s", value);
 349                         return;
 350                 }
 351                 while ((c = *value++) != '\0') {
 352                         if (escsep && ((c == os->os_fs || c == '\\')))
 353                                 (void) putchar('\\');
 354                         (void) putchar(c);
 355                 }
 356                 if (!os->os_lastfield)
 357                         (void) putchar(os->os_fs);
 358         } else if (multiline) {
 359                 if (value[0] == '\0')
 360                         value = OFMT_VAL_UNDEF;
 361                 (void) printf("%*.*s: %s", os->os_maxnamelen,
 362                     os->os_maxnamelen, ofp->of_name, value);
 363                 if (!os->os_lastfield)
 364                         (void) putchar('\n');
 365         } else {
 366                 if (os->os_lastfield) {
 367                         if (rightjust)
 368                                 (void) printf("%*s", width, value);
 369                         else
 370                                 (void) printf("%s", value);
 371                         os->os_overflow = 0;
 372                         return;
 373                 }
 374 
 375                 valwidth = strlen(value);
 376                 if (valwidth + os->os_overflow >= width) {
 377                         os->os_overflow += valwidth - width + 1;
 378                         if (rightjust)
 379                                 (void) printf("%*s ", width, value);
 380                         else
 381                                 (void) printf("%s ", value);
 382                         return;
 383                 }
 384 
 385                 if (os->os_overflow > 0) {
 386                         compress = MIN(os->os_overflow, width - valwidth);
 387                         os->os_overflow -= compress;
 388                         width -= compress;
 389                 }
 390                 if (rightjust)
 391                         (void) printf("%*s ", width, value);
 392                 else
 393                         (void) printf("%-*s", width, value);
 394         }
 395 }
 396 
 397 /*
 398  * Print enough to fit the field width.
 399  */
 400 static void
 401 ofmt_fit_width(split_t **spp, uint_t width, char *value, uint_t bufsize)
 402 {
 403         split_t         *sp = *spp;
 404         char            *ptr = value, *lim = ptr + bufsize;
 405         int             i, nextlen;
 406 
 407         if (sp == NULL) {
 408                 sp = split_str(value, OFMT_MAX_ROWS);
 409                 if (sp == NULL)
 410                         return;
 411 
 412                 *spp = sp;
 413         }
 414         for (i = sp->s_currfield; i < sp->s_nfields; i++) {
 415                 ptr += snprintf(ptr, lim - ptr, "%s,", sp->s_fields[i]);
 416                 if (i + 1 == sp->s_nfields) {
 417                         nextlen = 0;
 418                         if (ptr > value)
 419                                 ptr[-1] = '\0';
 420                 } else {
 421                         nextlen = strlen(sp->s_fields[i + 1]);
 422                 }
 423 
 424                 if (strlen(value) + nextlen > width || ptr >= lim) {
 425                         i++;
 426                         break;
 427                 }
 428         }
 429         sp->s_currfield = i;
 430 }
 431 
 432 /*
 433  * Print one or more rows of output values for the selected columns.
 434  */
 435 void
 436 ofmt_print(ofmt_handle_t ofmt, void *arg)
 437 {
 438         ofmt_state_t *os = ofmt;
 439         int i;
 440         char value[1024];
 441         ofmt_field_t *of;
 442         boolean_t escsep, more_rows;
 443         ofmt_arg_t ofarg;
 444         split_t **sp = NULL;
 445         boolean_t parsable = (os->os_flags & OFMT_PARSABLE);
 446         boolean_t multiline = (os->os_flags & OFMT_MULTILINE);
 447         boolean_t wrap = (os->os_flags & OFMT_WRAP);
 448 
 449         if (wrap) {
 450                 sp = calloc(sizeof (split_t *), os->os_nfields);
 451                 if (sp == NULL)
 452                         return;
 453         }
 454 
 455         if ((os->os_nrow++ % os->os_winsize.ws_row) == 0 &&
 456             !parsable && !multiline) {
 457                 if (!(os->os_flags & OFMT_NOHEADER))
 458                         ofmt_print_header(os);
 459                 os->os_nrow++;
 460         }
 461 
 462         if (multiline && os->os_nrow > 1)
 463                 (void) putchar('\n');
 464 
 465         of = os->os_fields;
 466         escsep = (os->os_nfields > 1);
 467         more_rows = B_FALSE;
 468         for (i = 0; i < os->os_nfields; i++) {
 469                 os->os_lastfield = (i + 1 == os->os_nfields);
 470                 value[0] = '\0';
 471                 ofarg.ofmt_id = of[i].of_id;
 472                 ofarg.ofmt_cbarg = arg;
 473 
 474                 if ((*of[i].of_cb)(&ofarg, value, sizeof (value))) {
 475                         if (wrap) {
 476                                 /*
 477                                  * 'value' will be split at comma boundaries
 478                                  * and stored into sp[i].
 479                                  */
 480                                 ofmt_fit_width(&sp[i], of[i].of_width, value,
 481                                     sizeof (value));
 482                                 if (sp[i] != NULL &&
 483                                     sp[i]->s_currfield < sp[i]->s_nfields)
 484                                         more_rows = B_TRUE;
 485                         }
 486 
 487                         ofmt_print_field(os, &of[i],
 488                             (*value == '\0' && !parsable) ?
 489                             OFMT_VAL_UNDEF : value, escsep);
 490                 } else {
 491                         ofmt_print_field(os, &of[i], OFMT_VAL_UNKNOWN, escsep);
 492                 }
 493         }
 494         (void) putchar('\n');
 495 
 496         while (more_rows) {
 497                 more_rows = B_FALSE;
 498                 for (i = 0; i < os->os_nfields; i++) {
 499                         os->os_lastfield = (i + 1 == os->os_nfields);
 500                         value[0] = '\0';
 501 
 502                         ofmt_fit_width(&sp[i], of[i].of_width,
 503                             value, sizeof (value));
 504                         if (sp[i] != NULL &&
 505                             sp[i]->s_currfield < sp[i]->s_nfields)
 506                                 more_rows = B_TRUE;
 507 
 508                         ofmt_print_field(os, &of[i], value, escsep);
 509                 }
 510                 (void) putchar('\n');
 511         }
 512         (void) fflush(stdout);
 513 
 514         if (sp != NULL) {
 515                 for (i = 0; i < os->os_nfields; i++)
 516                         splitfree(sp[i]);
 517                 free(sp);
 518         }
 519 }
 520 
 521 /*
 522  * Print the field headers
 523  */
 524 void
 525 ofmt_print_header(ofmt_handle_t ofmt)
 526 {
 527         ofmt_state_t *os = ofmt;
 528         int i;
 529         ofmt_field_t *of = os->os_fields;
 530         boolean_t escsep = (os->os_nfields > 1);
 531 
 532         for (i = 0; i < os->os_nfields; i++) {
 533                 os->os_lastfield = (i + 1 == os->os_nfields);
 534                 ofmt_print_field(os, &of[i], of[i].of_name, escsep);
 535         }
 536         (void) putchar('\n');
 537 }
 538 
 539 /*
 540  * Update the current window size.
 541  */
 542 void
 543 ofmt_update_winsize(ofmt_handle_t ofmt)
 544 {
 545         ofmt_state_t *os = ofmt;
 546         struct winsize *winsize = &os->os_winsize;
 547 
 548         if (ioctl(1, TIOCGWINSZ, winsize) == -1 ||
 549             winsize->ws_col == 0 || winsize->ws_row == 0) {
 550                 winsize->ws_col = 80;
 551                 winsize->ws_row = 24;
 552         }
 553 }
 554 
 555 /*
 556  * Return error diagnostics using the information in the ofmt_handle_t
 557  */
 558 char *
 559 ofmt_strerror(ofmt_handle_t ofmt, ofmt_status_t error, char *buf,
 560     uint_t bufsize)
 561 {
 562         ofmt_state_t *os = ofmt;
 563         int i;
 564         const char *s;
 565         char ebuf[OFMT_BUFSIZE];
 566         boolean_t parsable;
 567 
 568         /*
 569          * ebuf is intended for optional error-specific data to be appended
 570          * after the internationalized error string for an error code.
 571          */
 572         ebuf[0] = '\0';
 573 
 574         switch (error) {
 575         case OFMT_SUCCESS:
 576                 s = "success";
 577                 break;
 578         case OFMT_EBADFIELDS:
 579                 /*
 580                  * Enumerate the singular/plural version of the warning
 581                  * and error to simplify and improve localization.
 582                  */
 583                 parsable = (os->os_flags & OFMT_PARSABLE);
 584                 if (!parsable) {
 585                         if (os->os_nbad > 1)
 586                                 s = "ignoring unknown output fields:";
 587                         else
 588                                 s = "ignoring unknown output field:";
 589                 } else {
 590                         if (os->os_nbad > 1)
 591                                 s = "unknown output fields:";
 592                         else
 593                                 s = "unknown output field:";
 594                 }
 595                 /* set up the bad fields in ebuf */
 596                 for (i = 0; i < os->os_nbad; i++) {
 597                         (void) strlcat(ebuf, " `", sizeof (ebuf));
 598                         (void) strlcat(ebuf, os->os_badfields[i],
 599                             sizeof (ebuf));
 600                         (void) strlcat(ebuf, "'", sizeof (ebuf));
 601                 }
 602                 break;
 603         case OFMT_ENOFIELDS:
 604                 s = "no valid output fields";
 605                 break;
 606         case OFMT_EPARSEMULTI:
 607                 s = "multiline mode incompatible with parsable mode";
 608                 break;
 609         case OFMT_EPARSEALL:
 610                 s = "output field `all' invalid in parsable mode";
 611                 break;
 612         case OFMT_EPARSENONE:
 613                 s = "output fields must be specified in parsable mode";
 614                 break;
 615         case OFMT_EPARSEWRAP:
 616                 s = "parsable mode is incompatible with wrap mode";
 617                 break;
 618         case OFMT_ENOTEMPLATE:
 619                 s = "no template provided for fields";
 620                 break;
 621         case OFMT_ENOMEM:
 622                 s = strerror(ENOMEM);
 623                 break;
 624         default:
 625                 (void) snprintf(buf, bufsize,
 626                     dgettext(TEXT_DOMAIN, "unknown ofmt error (%d)"),
 627                     error);
 628                 return (buf);
 629         }
 630         (void) snprintf(buf, bufsize, dgettext(TEXT_DOMAIN, s));
 631         (void) strlcat(buf, ebuf, bufsize);
 632         return (buf);
 633 }
 634 
 635 void
 636 ofmt_check(ofmt_status_t oferr, boolean_t parsable, ofmt_handle_t ofmt,
 637     void (*die)(const char *, ...), void (*warn)(const char *, ...))
 638 {
 639         char buf[OFMT_BUFSIZE];
 640 
 641         assert(die != NULL);
 642         assert(warn != NULL);
 643 
 644         if (oferr == OFMT_SUCCESS)
 645                 return;
 646 
 647         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
 648 
 649         /*
 650          * All errors are considered fatal in parsable mode.  OFMT_ENOMEM and
 651          * OFMT_ENOFIELDS errors are always fatal, regardless of mode. For
 652          * other errors, we print diagnostics in human-readable mode and
 653          * processs what we can.
 654          */
 655         if (parsable || oferr == OFMT_ENOFIELDS || oferr == OFMT_ENOMEM) {
 656                 ofmt_close(ofmt);
 657                 die(buf);
 658         } else {
 659                 warn(buf);
 660         }
 661 }