1*5088Sab196087 /* 2*5088Sab196087 * CDDL HEADER START 3*5088Sab196087 * 4*5088Sab196087 * The contents of this file are subject to the terms of the 5*5088Sab196087 * Common Development and Distribution License (the "License"). 6*5088Sab196087 * You may not use this file except in compliance with the License. 7*5088Sab196087 * 8*5088Sab196087 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*5088Sab196087 * or http://www.opensolaris.org/os/licensing. 10*5088Sab196087 * See the License for the specific language governing permissions 11*5088Sab196087 * and limitations under the License. 12*5088Sab196087 * 13*5088Sab196087 * When distributing Covered Code, include this CDDL HEADER in each 14*5088Sab196087 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*5088Sab196087 * If applicable, add the following below this CDDL HEADER, with the 16*5088Sab196087 * fields enclosed by brackets "[]" replaced with your own identifying 17*5088Sab196087 * information: Portions Copyright [yyyy] [name of copyright owner] 18*5088Sab196087 * 19*5088Sab196087 * CDDL HEADER END 20*5088Sab196087 */ 21*5088Sab196087 22*5088Sab196087 /* 23*5088Sab196087 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24*5088Sab196087 * Use is subject to license terms. 25*5088Sab196087 */ 26*5088Sab196087 #pragma ident "%Z%%M% %I% %E% SMI" 27*5088Sab196087 28*5088Sab196087 #include <sys/types.h> 29*5088Sab196087 #include <sys/stat.h> 30*5088Sab196087 #include <sys/wait.h> 31*5088Sab196087 #include <stdarg.h> 32*5088Sab196087 #include <fcntl.h> 33*5088Sab196087 #include <stdlib.h> 34*5088Sab196087 #include <stdio.h> 35*5088Sab196087 #include <signal.h> 36*5088Sab196087 #include <dirent.h> 37*5088Sab196087 #include <libelf.h> 38*5088Sab196087 #include <gelf.h> 39*5088Sab196087 #include <conv.h> 40*5088Sab196087 #include <dlfcn.h> 41*5088Sab196087 #include <link.h> 42*5088Sab196087 #include <stdarg.h> 43*5088Sab196087 #include <libgen.h> 44*5088Sab196087 #include <libintl.h> 45*5088Sab196087 #include <locale.h> 46*5088Sab196087 #include <unistd.h> 47*5088Sab196087 #include <errno.h> 48*5088Sab196087 #include <ctype.h> 49*5088Sab196087 #include <limits.h> 50*5088Sab196087 #include <strings.h> 51*5088Sab196087 #include <sgs.h> 52*5088Sab196087 #include "msg.h" 53*5088Sab196087 #include "_elfedit.h" 54*5088Sab196087 #include <debug.h> /* liblddb */ 55*5088Sab196087 56*5088Sab196087 57*5088Sab196087 58*5088Sab196087 /* 59*5088Sab196087 * Column at which elfedit_format_command_usage() will wrap the 60*5088Sab196087 * generated usage string if the wrap argument is True (1). 61*5088Sab196087 */ 62*5088Sab196087 #define USAGE_WRAP_COL 55 63*5088Sab196087 64*5088Sab196087 65*5088Sab196087 66*5088Sab196087 67*5088Sab196087 /* 68*5088Sab196087 * Type used to represent a string buffer that can grow as needed 69*5088Sab196087 * to hold strings of arbitrary length. The user should declare 70*5088Sab196087 * variables of this type sa static. The strbuf_ensure_size() function 71*5088Sab196087 * is used to ensure that it has a minimum desired size. 72*5088Sab196087 */ 73*5088Sab196087 typedef struct { 74*5088Sab196087 char *buf; /* String buffer */ 75*5088Sab196087 size_t n; /* Size of buffer */ 76*5088Sab196087 } STRBUF; 77*5088Sab196087 78*5088Sab196087 79*5088Sab196087 80*5088Sab196087 81*5088Sab196087 /* 82*5088Sab196087 * Types used by tokenize_user_cmd() to represent the result of 83*5088Sab196087 * spliting a user command into individual tokens. 84*5088Sab196087 */ 85*5088Sab196087 typedef struct { 86*5088Sab196087 char *tok_str; /* Token string */ 87*5088Sab196087 size_t tok_len; /* strlen(str) */ 88*5088Sab196087 size_t tok_line_off; /* Token offset in original string */ 89*5088Sab196087 } TOK_ELT; 90*5088Sab196087 typedef struct { 91*5088Sab196087 size_t tokst_cmd_len; /* Length of original user command, without */ 92*5088Sab196087 /* newline or NULL termination chars */ 93*5088Sab196087 size_t tokst_str_size; /* Space needed to hold all the resulting */ 94*5088Sab196087 /* tokens, including terminating NULL */ 95*5088Sab196087 TOK_ELT *tokst_buf; /* The array of tokens */ 96*5088Sab196087 size_t tokst_cnt; /* # of tokens in array */ 97*5088Sab196087 size_t tokst_bufsize; /* capacity of array */ 98*5088Sab196087 } TOK_STATE; 99*5088Sab196087 100*5088Sab196087 101*5088Sab196087 102*5088Sab196087 103*5088Sab196087 /* State block used by gettok_init() and gettok() */ 104*5088Sab196087 typedef struct { 105*5088Sab196087 const char *gtok_buf; /* Addr of buffer containing string */ 106*5088Sab196087 char *gtok_cur_buf; /* Addr withing buffer for next token */ 107*5088Sab196087 int gtok_inc_null_final; /* True if final NULL token used */ 108*5088Sab196087 int gtok_null_seen; /* True when NULL byte seen */ 109*5088Sab196087 TOK_ELT gtok_last_token; /* Last token parsed */ 110*5088Sab196087 111*5088Sab196087 } GETTOK_STATE; 112*5088Sab196087 113*5088Sab196087 114*5088Sab196087 115*5088Sab196087 116*5088Sab196087 /* 117*5088Sab196087 * The elfedit_cpl_*() functions are used for command line completion. 118*5088Sab196087 * Currently this uses the tecla library, but to allow for changing the 119*5088Sab196087 * library used, we hide all tecla interfaces from our modules. Instead, 120*5088Sab196087 * cmd_match_fcn() builds an ELFEDIT_CPL_STATE struct, and we pass the 121*5088Sab196087 * address of that struct as an opaque handle to the modules. Since the 122*5088Sab196087 * pointer is opaque, the contents of ELFEDIT_CPL_STATE are free to change 123*5088Sab196087 * as necessary. 124*5088Sab196087 */ 125*5088Sab196087 typedef struct { 126*5088Sab196087 WordCompletion *ecpl_cpl; /* tecla handle */ 127*5088Sab196087 const char *ecpl_line; /* raw input line */ 128*5088Sab196087 int ecpl_word_start; /* start offset within line */ 129*5088Sab196087 int ecpl_word_end; /* offset just past token */ 130*5088Sab196087 /* 131*5088Sab196087 * ecpl_add_mod_colon is a secret handshake between 132*5088Sab196087 * elfedit_cpl_command() and elfedit_cpl_add_match(). It adds 133*5088Sab196087 * ':' to end of matched modules. 134*5088Sab196087 */ 135*5088Sab196087 int ecpl_add_mod_colon; 136*5088Sab196087 const char *ecpl_token_str; /* token being completed */ 137*5088Sab196087 size_t ecpl_token_len; /* strlen(ecpl_token_str) */ 138*5088Sab196087 } ELFEDIT_CPL_STATE; 139*5088Sab196087 140*5088Sab196087 141*5088Sab196087 142*5088Sab196087 143*5088Sab196087 /* This structure maintains elfedit global state */ 144*5088Sab196087 STATE_T state; 145*5088Sab196087 146*5088Sab196087 147*5088Sab196087 148*5088Sab196087 /* 149*5088Sab196087 * Define a pair of static global variables that contain the 150*5088Sab196087 * ISA strings that correspond to %i and %I tokens in module search 151*5088Sab196087 * paths. 152*5088Sab196087 * 153*5088Sab196087 * isa_i_str - The ISA string for the currently running program 154*5088Sab196087 * isa_I_str - For 64-bit programs, the same as isa_i_str. For 155*5088Sab196087 * 32-bit programs, an empty string. 156*5088Sab196087 */ 157*5088Sab196087 #ifdef __sparc 158*5088Sab196087 #ifdef __sparcv9 159*5088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_64); 160*5088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_SPARC_64); 161*5088Sab196087 #else 162*5088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_SPARC_32); 163*5088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY); 164*5088Sab196087 #endif 165*5088Sab196087 #endif 166*5088Sab196087 167*5088Sab196087 #ifdef __i386 168*5088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_32); 169*5088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_STR_EMPTY); 170*5088Sab196087 #endif 171*5088Sab196087 #ifdef __amd64 172*5088Sab196087 static const char *isa_i_str = MSG_ORIG(MSG_ISA_X86_64); 173*5088Sab196087 static const char *isa_I_str = MSG_ORIG(MSG_ISA_X86_64); 174*5088Sab196087 #endif 175*5088Sab196087 176*5088Sab196087 177*5088Sab196087 178*5088Sab196087 /* Forward declarations */ 179*5088Sab196087 static void free_user_cmds(void); 180*5088Sab196087 static void elfedit_pager_cleanup(void); 181*5088Sab196087 182*5088Sab196087 183*5088Sab196087 184*5088Sab196087 /* 185*5088Sab196087 * We supply this function for the msg module 186*5088Sab196087 */ 187*5088Sab196087 const char * 188*5088Sab196087 _elfedit_msg(Msg mid) 189*5088Sab196087 { 190*5088Sab196087 return (gettext(MSG_ORIG(mid))); 191*5088Sab196087 } 192*5088Sab196087 193*5088Sab196087 194*5088Sab196087 /* 195*5088Sab196087 * Copy at most min(cpsize, dstsize-1) bytes from src into dst, 196*5088Sab196087 * truncating src if necessary. The result is always null-terminated. 197*5088Sab196087 * 198*5088Sab196087 * entry: 199*5088Sab196087 * dst - Destination buffer 200*5088Sab196087 * src - Source string 201*5088Sab196087 * dstsize - sizeof(dst) 202*5088Sab196087 * 203*5088Sab196087 * note: 204*5088Sab196087 * This is similar to strncpy(), but with two modifications: 205*5088Sab196087 * 1) You specify the number of characters to copy, not just 206*5088Sab196087 * the size of the destination. Hence, you can copy non-NULL 207*5088Sab196087 * terminated strings. 208*5088Sab196087 * 2) The destination is guaranteed to be NULL terminated. strncpy() 209*5088Sab196087 * does not terminate a completely full buffer. 210*5088Sab196087 */ 211*5088Sab196087 static void 212*5088Sab196087 elfedit_strnbcpy(char *dst, const char *src, size_t cpsize, size_t dstsize) 213*5088Sab196087 { 214*5088Sab196087 if (cpsize >= dstsize) 215*5088Sab196087 cpsize = dstsize - 1; 216*5088Sab196087 if (cpsize > 0) 217*5088Sab196087 (void) strncpy(dst, src, cpsize + 1); 218*5088Sab196087 dst[cpsize] = '\0'; 219*5088Sab196087 } 220*5088Sab196087 221*5088Sab196087 222*5088Sab196087 /* 223*5088Sab196087 * Calls exit() on behalf of elfedit. 224*5088Sab196087 */ 225*5088Sab196087 void 226*5088Sab196087 elfedit_exit(int status) 227*5088Sab196087 { 228*5088Sab196087 if (state.file.present) { 229*5088Sab196087 /* Exiting with unflushed changes pending? Issue debug notice */ 230*5088Sab196087 if (state.file.dirty) 231*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 232*5088Sab196087 MSG_INTL(MSG_DEBUG_DIRTYEXIT)); 233*5088Sab196087 234*5088Sab196087 /* 235*5088Sab196087 * If the edit file is marked for unlink on exit, then 236*5088Sab196087 * take care of it here. 237*5088Sab196087 */ 238*5088Sab196087 if (state.file.unlink_on_exit) { 239*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 240*5088Sab196087 MSG_INTL(MSG_DEBUG_UNLINKFILE), 241*5088Sab196087 state.file.outfile); 242*5088Sab196087 (void) unlink(state.file.outfile); 243*5088Sab196087 } 244*5088Sab196087 } 245*5088Sab196087 246*5088Sab196087 exit(status); 247*5088Sab196087 } 248*5088Sab196087 249*5088Sab196087 250*5088Sab196087 /* 251*5088Sab196087 * Standard message function for elfedit. All user visible 252*5088Sab196087 * output, for error or informational reasons, should go through 253*5088Sab196087 * this function. 254*5088Sab196087 * 255*5088Sab196087 * entry: 256*5088Sab196087 * type - Type of message. One of the ELFEDIT_MSG_* values. 257*5088Sab196087 * format, ... - As per the printf() family 258*5088Sab196087 * 259*5088Sab196087 * exit: 260*5088Sab196087 * The desired message has been output. For informational 261*5088Sab196087 * messages, control returns to the caller. For errors, 262*5088Sab196087 * this routine will terminate execution or strip the execution 263*5088Sab196087 * stack and return control directly to the outer control loop. 264*5088Sab196087 * In either case, the caller will not receive control. 265*5088Sab196087 */ 266*5088Sab196087 /*PRINTFLIKE2*/ 267*5088Sab196087 void 268*5088Sab196087 elfedit_msg(elfedit_msg_t type, const char *format, ...) 269*5088Sab196087 { 270*5088Sab196087 typedef enum { /* What to do after finished */ 271*5088Sab196087 DISP_RET = 0, /* Return to caller */ 272*5088Sab196087 DISP_JMP = 1, /* if (interactive) longjmp else exit */ 273*5088Sab196087 DISP_EXIT = 2 /* exit under all circumstances */ 274*5088Sab196087 } DISP; 275*5088Sab196087 276*5088Sab196087 va_list args; 277*5088Sab196087 FILE *stream = stderr; 278*5088Sab196087 DISP disp = DISP_RET; 279*5088Sab196087 int do_output = 1; 280*5088Sab196087 int need_prefix = 1; 281*5088Sab196087 282*5088Sab196087 va_start(args, format); 283*5088Sab196087 284*5088Sab196087 switch (type) { 285*5088Sab196087 case ELFEDIT_MSG_ERR: 286*5088Sab196087 case ELFEDIT_MSG_CMDUSAGE: 287*5088Sab196087 disp = DISP_JMP; 288*5088Sab196087 break; 289*5088Sab196087 case ELFEDIT_MSG_FATAL: 290*5088Sab196087 disp = DISP_EXIT; 291*5088Sab196087 break; 292*5088Sab196087 case ELFEDIT_MSG_USAGE: 293*5088Sab196087 need_prefix = 0; 294*5088Sab196087 break; 295*5088Sab196087 case ELFEDIT_MSG_DEBUG: 296*5088Sab196087 if (!(state.flags & ELFEDIT_F_DEBUG)) 297*5088Sab196087 return; 298*5088Sab196087 stream = stdout; 299*5088Sab196087 break; 300*5088Sab196087 case ELFEDIT_MSG_QUIET: 301*5088Sab196087 do_output = 0; 302*5088Sab196087 disp = DISP_JMP; 303*5088Sab196087 break; 304*5088Sab196087 } 305*5088Sab196087 306*5088Sab196087 307*5088Sab196087 /* 308*5088Sab196087 * If there is a pager process running, we are returning to the 309*5088Sab196087 * caller, and the output is going to stdout, then let the 310*5088Sab196087 * pager handle it instead of writing it directly from this process. 311*5088Sab196087 * That way, the output gets paged along with everything else. 312*5088Sab196087 * 313*5088Sab196087 * If there is a pager process running, and we are not returning 314*5088Sab196087 * to the caller, then end the pager process now, before we generate 315*5088Sab196087 * any new output. This allows for any text buffered in the pager 316*5088Sab196087 * pipe to be output before the new stuff. 317*5088Sab196087 */ 318*5088Sab196087 if (state.pager.fptr != NULL) { 319*5088Sab196087 if (disp == DISP_RET) { 320*5088Sab196087 if (stream == stdout) 321*5088Sab196087 stream = state.pager.fptr; 322*5088Sab196087 } else { 323*5088Sab196087 elfedit_pager_cleanup(); 324*5088Sab196087 } 325*5088Sab196087 } 326*5088Sab196087 327*5088Sab196087 /* 328*5088Sab196087 * If this message is coming from within the libtecla command 329*5088Sab196087 * completion code, call gl_normal_io() to give the library notice. 330*5088Sab196087 * That function sets the tty back to cooked mode and advances 331*5088Sab196087 * the cursor to the beginning of the next line so that our output 332*5088Sab196087 * will appear properly. When we return to the command completion code, 333*5088Sab196087 * tecla will re-enter raw mode and redraw the current command line. 334*5088Sab196087 */ 335*5088Sab196087 if (state.input.in_tecla) 336*5088Sab196087 (void) gl_normal_io(state.input.gl); 337*5088Sab196087 338*5088Sab196087 if (do_output) { 339*5088Sab196087 if (need_prefix) 340*5088Sab196087 (void) fprintf(stream, MSG_ORIG(MSG_STR_ELFEDIT)); 341*5088Sab196087 (void) vfprintf(stream, format, args); 342*5088Sab196087 (void) fflush(stream); 343*5088Sab196087 } 344*5088Sab196087 va_end(args); 345*5088Sab196087 346*5088Sab196087 /* 347*5088Sab196087 * If this is an error, then we do not return to the caller. 348*5088Sab196087 * The action taken depends on whether the outer loop has registered 349*5088Sab196087 * a jump buffer for us or not. 350*5088Sab196087 */ 351*5088Sab196087 if (disp != DISP_RET) { 352*5088Sab196087 if (state.msg_jbuf.active && (disp == DISP_JMP)) { 353*5088Sab196087 /* Free the user command list */ 354*5088Sab196087 free_user_cmds(); 355*5088Sab196087 356*5088Sab196087 /* Clean up to reflect effect of non-local goto */ 357*5088Sab196087 state.input.in_tecla = FALSE; 358*5088Sab196087 359*5088Sab196087 /* Jump to the outer loop to resume */ 360*5088Sab196087 siglongjmp(state.msg_jbuf.env, 1); 361*5088Sab196087 } else { 362*5088Sab196087 elfedit_exit(1); 363*5088Sab196087 } 364*5088Sab196087 } 365*5088Sab196087 } 366*5088Sab196087 367*5088Sab196087 368*5088Sab196087 /* 369*5088Sab196087 * Wrapper on elfedit_msg() that issues an error that results from 370*5088Sab196087 * a call to libelf. 371*5088Sab196087 * 372*5088Sab196087 * entry: 373*5088Sab196087 * file - Name of ELF object 374*5088Sab196087 * libelf_rtn_name - Name of routine that was called 375*5088Sab196087 * 376*5088Sab196087 * exit: 377*5088Sab196087 * An error has been issued that shows the routine called 378*5088Sab196087 * and the libelf error string for it from elf_errmsg(). 379*5088Sab196087 * This routine does not return to the caller. 380*5088Sab196087 */ 381*5088Sab196087 void 382*5088Sab196087 elfedit_elferr(const char *file, const char *libelf_rtn_name) 383*5088Sab196087 { 384*5088Sab196087 const char *errstr = elf_errmsg(elf_errno()); 385*5088Sab196087 386*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF), file, 387*5088Sab196087 libelf_rtn_name, errstr ? errstr : MSG_INTL(MSG_FMT_UNKNOWN)); 388*5088Sab196087 } 389*5088Sab196087 390*5088Sab196087 391*5088Sab196087 /* 392*5088Sab196087 * Start an output pager process for elfedit_printf()/elfedit_write() to use. 393*5088Sab196087 * 394*5088Sab196087 * note: 395*5088Sab196087 * If this elfedit session is not interactive, then no pager is 396*5088Sab196087 * started. Paging is only intended for interactive use. The caller 397*5088Sab196087 * is not supposed to worry about this point, but simply to use 398*5088Sab196087 * this function to flag situations in which paging might be needed. 399*5088Sab196087 */ 400*5088Sab196087 void 401*5088Sab196087 elfedit_pager_init(void) 402*5088Sab196087 { 403*5088Sab196087 const char *errstr; 404*5088Sab196087 const char *cmd; 405*5088Sab196087 int err; 406*5088Sab196087 407*5088Sab196087 /* 408*5088Sab196087 * If there is no pager process running, start one. 409*5088Sab196087 * Only do this for interactive sessions --- elfedit_pager() 410*5088Sab196087 * won't use a pager in batch mode. 411*5088Sab196087 */ 412*5088Sab196087 if (state.msg_jbuf.active && state.input.full_tty && 413*5088Sab196087 (state.pager.fptr == NULL)) { 414*5088Sab196087 /* 415*5088Sab196087 * If the user has the PAGER environment variable set, 416*5088Sab196087 * then we will use that program. Otherwise we default 417*5088Sab196087 * to /bin/more. 418*5088Sab196087 */ 419*5088Sab196087 cmd = getenv(MSG_ORIG(MSG_STR_PAGER)); 420*5088Sab196087 if ((cmd == NULL) || (*cmd == '\0')) 421*5088Sab196087 cmd = MSG_ORIG(MSG_STR_BINMORE); 422*5088Sab196087 423*5088Sab196087 /* 424*5088Sab196087 * The popen() manpage says that on failure, it "may set errno", 425*5088Sab196087 * which is somewhat ambiguous. We explicitly zero it here, and 426*5088Sab196087 * assume that any change is due to popen() failing. 427*5088Sab196087 */ 428*5088Sab196087 errno = 0; 429*5088Sab196087 state.pager.fptr = popen(cmd, MSG_ORIG(MSG_STR_W)); 430*5088Sab196087 if (state.pager.fptr == NULL) { 431*5088Sab196087 err = errno; 432*5088Sab196087 errstr = (err == 0) ? MSG_INTL(MSG_ERR_UNKNOWNSYSERR) : 433*5088Sab196087 strerror(err); 434*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTEXEC), 435*5088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT), cmd, errstr); 436*5088Sab196087 } 437*5088Sab196087 } 438*5088Sab196087 } 439*5088Sab196087 440*5088Sab196087 441*5088Sab196087 /* 442*5088Sab196087 * If there is a pager process present, close it out. 443*5088Sab196087 * 444*5088Sab196087 * note: 445*5088Sab196087 * This function is called from within elfedit_msg(), and as 446*5088Sab196087 * such, must not use elfedit_msg() to report errors. Furthermore, 447*5088Sab196087 * any such errors are not a sufficient reason to terminate the process 448*5088Sab196087 * or to longjmp(). This is a rare case where errors are written 449*5088Sab196087 * directly to stderr. 450*5088Sab196087 */ 451*5088Sab196087 static void 452*5088Sab196087 elfedit_pager_cleanup(void) 453*5088Sab196087 { 454*5088Sab196087 if (state.pager.fptr != NULL) { 455*5088Sab196087 if (pclose(state.pager.fptr) == -1) 456*5088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_PAGERFINI)); 457*5088Sab196087 458*5088Sab196087 state.pager.fptr = NULL; 459*5088Sab196087 } 460*5088Sab196087 } 461*5088Sab196087 462*5088Sab196087 463*5088Sab196087 /* 464*5088Sab196087 * Print general formtted text for the user, using printf()-style 465*5088Sab196087 * formatting. Uses the pager process if one has been started, or 466*5088Sab196087 * stdout otherwise. 467*5088Sab196087 */ 468*5088Sab196087 void 469*5088Sab196087 elfedit_printf(const char *format, ...) 470*5088Sab196087 { 471*5088Sab196087 va_list args; 472*5088Sab196087 int err; 473*5088Sab196087 FILE *fptr; 474*5088Sab196087 int pager; 475*5088Sab196087 int broken_pipe = 0; 476*5088Sab196087 477*5088Sab196087 /* 478*5088Sab196087 * If there is a pager process, then use it. Otherwise write 479*5088Sab196087 * directly to stdout. 480*5088Sab196087 */ 481*5088Sab196087 pager = (state.pager.fptr != NULL); 482*5088Sab196087 fptr = pager ? state.pager.fptr : stdout; 483*5088Sab196087 484*5088Sab196087 va_start(args, format); 485*5088Sab196087 errno = 0; 486*5088Sab196087 err = vfprintf(fptr, format, args); 487*5088Sab196087 488*5088Sab196087 /* Did we fail because a child pager process has exited? */ 489*5088Sab196087 broken_pipe = pager && (err < 0) && (errno == EPIPE); 490*5088Sab196087 491*5088Sab196087 va_end(args); 492*5088Sab196087 493*5088Sab196087 /* 494*5088Sab196087 * On error, we simply issue the error without cleaning up 495*5088Sab196087 * the pager process. The message code handles that as a standard 496*5088Sab196087 * part of error processing. 497*5088Sab196087 * 498*5088Sab196087 * We handle failure due to an exited pager process differently 499*5088Sab196087 * than a normal error, because it is usually due to the user 500*5088Sab196087 * intentionally telling it to. 501*5088Sab196087 */ 502*5088Sab196087 if (err < 0) { 503*5088Sab196087 if (broken_pipe) 504*5088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL)); 505*5088Sab196087 else 506*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF)); 507*5088Sab196087 } 508*5088Sab196087 } 509*5088Sab196087 510*5088Sab196087 511*5088Sab196087 /* 512*5088Sab196087 * Some our modules use liblddb routines to format ELF output. 513*5088Sab196087 * In order to ensure that such output is sent to the pager pipe 514*5088Sab196087 * when there is one, and stdout otherwise, we redefine the dbg_print() 515*5088Sab196087 * function here. 516*5088Sab196087 * 517*5088Sab196087 * This item should be defined NODIRECT. 518*5088Sab196087 */ 519*5088Sab196087 /* PRINTFLIKE2 */ 520*5088Sab196087 void 521*5088Sab196087 dbg_print(Lm_list *lml, const char *format, ...) 522*5088Sab196087 { 523*5088Sab196087 va_list ap; 524*5088Sab196087 int err; 525*5088Sab196087 FILE *fptr; 526*5088Sab196087 int pager; 527*5088Sab196087 int broken_pipe = 0; 528*5088Sab196087 529*5088Sab196087 #if defined(lint) 530*5088Sab196087 /* 531*5088Sab196087 * The lml argument is only meaningful for diagnostics sent to ld.so.1. 532*5088Sab196087 * Supress the lint error by making a dummy assignment. 533*5088Sab196087 */ 534*5088Sab196087 lml = 0; 535*5088Sab196087 #endif 536*5088Sab196087 537*5088Sab196087 /* 538*5088Sab196087 * If there is a pager process, then use it. Otherwise write 539*5088Sab196087 * directly to stdout. 540*5088Sab196087 */ 541*5088Sab196087 pager = (state.pager.fptr != NULL); 542*5088Sab196087 fptr = pager ? state.pager.fptr : stdout; 543*5088Sab196087 544*5088Sab196087 va_start(ap, format); 545*5088Sab196087 errno = 0; 546*5088Sab196087 err = vfprintf(fptr, format, ap); 547*5088Sab196087 if (err >= 0) 548*5088Sab196087 err = fprintf(fptr, MSG_ORIG(MSG_STR_NL)); 549*5088Sab196087 550*5088Sab196087 /* Did we fail because a child pager process has exited? */ 551*5088Sab196087 broken_pipe = (err < 0) && pager && (errno == EPIPE); 552*5088Sab196087 553*5088Sab196087 va_end(ap); 554*5088Sab196087 555*5088Sab196087 /* 556*5088Sab196087 * On error, we simply issue the error without cleaning up 557*5088Sab196087 * the pager process. The message code handles that as a standard 558*5088Sab196087 * part of error processing. 559*5088Sab196087 * 560*5088Sab196087 * We handle failure due to an exited pager process differently 561*5088Sab196087 * than a normal error, because it is usually due to the user 562*5088Sab196087 * intentionally telling it to. 563*5088Sab196087 */ 564*5088Sab196087 if (err < 0) { 565*5088Sab196087 if (broken_pipe) 566*5088Sab196087 elfedit_msg(ELFEDIT_MSG_QUIET, MSG_ORIG(MSG_STR_NULL)); 567*5088Sab196087 else 568*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_PRINTF)); 569*5088Sab196087 } 570*5088Sab196087 } 571*5088Sab196087 572*5088Sab196087 573*5088Sab196087 /* 574*5088Sab196087 * Write raw bytes of text in a manner similar to fwrite(). 575*5088Sab196087 * Uses the pager process if one has been started, or 576*5088Sab196087 * stdout otherwise. 577*5088Sab196087 */ 578*5088Sab196087 void 579*5088Sab196087 elfedit_write(const void *ptr, size_t size) 580*5088Sab196087 { 581*5088Sab196087 FILE *fptr; 582*5088Sab196087 int err; 583*5088Sab196087 584*5088Sab196087 /* 585*5088Sab196087 * If there is a pager process, then use it. Otherwise write 586*5088Sab196087 * directly to stdout. 587*5088Sab196087 */ 588*5088Sab196087 fptr = (state.pager.fptr == NULL) ? stdout : state.pager.fptr; 589*5088Sab196087 590*5088Sab196087 if (fwrite(ptr, 1, size, fptr) != size) { 591*5088Sab196087 err = errno; 592*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_FWRITE), 593*5088Sab196087 strerror(err)); 594*5088Sab196087 } 595*5088Sab196087 } 596*5088Sab196087 597*5088Sab196087 598*5088Sab196087 /* 599*5088Sab196087 * Wrappers on malloc() and realloc() that check the result for success 600*5088Sab196087 * and issue an error if not. The caller can use the result of these 601*5088Sab196087 * functions without checking for a NULL pointer, as we do not return to 602*5088Sab196087 * the caller in the failure case. 603*5088Sab196087 */ 604*5088Sab196087 void * 605*5088Sab196087 elfedit_malloc(const char *item_name, size_t size) 606*5088Sab196087 { 607*5088Sab196087 void *m; 608*5088Sab196087 609*5088Sab196087 m = malloc(size); 610*5088Sab196087 if (m == NULL) { 611*5088Sab196087 int err = errno; 612*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC), 613*5088Sab196087 item_name, strerror(err)); 614*5088Sab196087 } 615*5088Sab196087 616*5088Sab196087 return (m); 617*5088Sab196087 } 618*5088Sab196087 619*5088Sab196087 void * 620*5088Sab196087 elfedit_realloc(const char *item_name, void *ptr, size_t size) 621*5088Sab196087 { 622*5088Sab196087 void *m; 623*5088Sab196087 624*5088Sab196087 m = realloc(ptr, size); 625*5088Sab196087 if (m == NULL) { 626*5088Sab196087 int err = errno; 627*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_MALLOC), 628*5088Sab196087 item_name, strerror(err)); 629*5088Sab196087 } 630*5088Sab196087 631*5088Sab196087 return (m); 632*5088Sab196087 } 633*5088Sab196087 634*5088Sab196087 635*5088Sab196087 /* 636*5088Sab196087 * Ensure that the given buffer has room for n bytes of data. 637*5088Sab196087 */ 638*5088Sab196087 static void 639*5088Sab196087 strbuf_ensure_size(STRBUF *str, size_t size) 640*5088Sab196087 { 641*5088Sab196087 #define INITIAL_STR_ALLOC 128 642*5088Sab196087 643*5088Sab196087 size_t n; 644*5088Sab196087 645*5088Sab196087 n = (str->n == 0) ? INITIAL_STR_ALLOC : str->n; 646*5088Sab196087 while (size > n) /* Double buffer until string fits */ 647*5088Sab196087 n *= 2; 648*5088Sab196087 if (n != str->n) { /* Alloc new string buffer if needed */ 649*5088Sab196087 str->buf = elfedit_realloc(MSG_INTL(MSG_ALLOC_UCMDSTR), 650*5088Sab196087 str->buf, n); 651*5088Sab196087 str->n = n; 652*5088Sab196087 } 653*5088Sab196087 654*5088Sab196087 #undef INITIAL_STR_ALLOC 655*5088Sab196087 } 656*5088Sab196087 657*5088Sab196087 658*5088Sab196087 /* 659*5088Sab196087 * Extract the argument/option information for the next item referenced 660*5088Sab196087 * by optarg, and advance the pointer to the next item. 661*5088Sab196087 * 662*5088Sab196087 * entry: 663*5088Sab196087 * optarg - Address of pointer to argument or option array 664*5088Sab196087 * item - Struct to be filled in. 665*5088Sab196087 * 666*5088Sab196087 * exit: 667*5088Sab196087 * The item block has been filled in with the information for 668*5088Sab196087 * the next item in the optarg array. *optarg has been advanced 669*5088Sab196087 * to the next item. 670*5088Sab196087 */ 671*5088Sab196087 void 672*5088Sab196087 elfedit_next_optarg(elfedit_cmd_optarg_t **optarg, elfedit_optarg_item_t *item) 673*5088Sab196087 { 674*5088Sab196087 /* 675*5088Sab196087 * Array of inheritable options/arguments. Indexed by one less 676*5088Sab196087 * than the corresponding ELFEDIT_STDOA_ value. 677*5088Sab196087 */ 678*5088Sab196087 static const elfedit_optarg_item_t stdoa[] = { 679*5088Sab196087 /* ELFEDIT_STDOA_O */ 680*5088Sab196087 { MSG_ORIG(MSG_STR_MINUS_O), MSG_ORIG(MSG_STR_OUTSTYLE), 681*5088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_O) */ 682*5088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_O, 683*5088Sab196087 ELFEDIT_CMDOA_F_VALUE }, 684*5088Sab196087 685*5088Sab196087 /* ELFEDIT_STDOA_AND */ 686*5088Sab196087 { MSG_ORIG(MSG_STR_MINUS_AND), NULL, 687*5088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_AND) */ 688*5088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_AND, 0 }, 689*5088Sab196087 690*5088Sab196087 /* ELFEDIT_STDOA_CMP */ 691*5088Sab196087 { MSG_ORIG(MSG_STR_MINUS_CMP), NULL, 692*5088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_CMP) */ 693*5088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_CMP, 0 }, 694*5088Sab196087 695*5088Sab196087 /* ELFEDIT_STDOA_OR */ 696*5088Sab196087 { MSG_ORIG(MSG_STR_MINUS_OR), NULL, 697*5088Sab196087 /* MSG_INTL(MSG_STDOA_OPTDESC_OR) */ 698*5088Sab196087 (elfedit_i18nhdl_t)MSG_STDOA_OPTDESC_OR, 0 }, 699*5088Sab196087 }; 700*5088Sab196087 701*5088Sab196087 elfedit_cmd_optarg_t *oa; 702*5088Sab196087 703*5088Sab196087 704*5088Sab196087 /* Grab first item, advance the callers pointer over it */ 705*5088Sab196087 oa = (*optarg)++; 706*5088Sab196087 707*5088Sab196087 if (oa->oa_flags & ELFEDIT_CMDOA_F_INHERIT) { 708*5088Sab196087 /* Values are pre-chewed in the stdoa array above */ 709*5088Sab196087 *item = stdoa[((uintptr_t)oa->oa_name) - 1]; 710*5088Sab196087 711*5088Sab196087 /* 712*5088Sab196087 * Set the inherited flag so that elfedit_optarg_helpstr() 713*5088Sab196087 * can tell who is responsible for translating the help string. 714*5088Sab196087 */ 715*5088Sab196087 item->oai_flags |= ELFEDIT_CMDOA_F_INHERIT; 716*5088Sab196087 } else { /* Non-inherited item */ 717*5088Sab196087 item->oai_name = oa->oa_name; 718*5088Sab196087 if ((oa->oa_flags & ELFEDIT_CMDOA_F_VALUE) != 0) { 719*5088Sab196087 item->oai_vname = oa[1].oa_name; 720*5088Sab196087 721*5088Sab196087 /* Advance users pointer past value element */ 722*5088Sab196087 (*optarg)++; 723*5088Sab196087 } else { 724*5088Sab196087 item->oai_vname = NULL; 725*5088Sab196087 } 726*5088Sab196087 item->oai_help = oa->oa_help; 727*5088Sab196087 item->oai_flags = oa->oa_flags; 728*5088Sab196087 } 729*5088Sab196087 730*5088Sab196087 /* 731*5088Sab196087 * The module determines the idmask and excmask fields whether 732*5088Sab196087 * or not inheritance is in play. 733*5088Sab196087 */ 734*5088Sab196087 item->oai_idmask = oa->oa_idmask; 735*5088Sab196087 item->oai_excmask = oa->oa_excmask; 736*5088Sab196087 } 737*5088Sab196087 738*5088Sab196087 739*5088Sab196087 740*5088Sab196087 /* 741*5088Sab196087 * Return the help string for an option/argument item, as returned 742*5088Sab196087 * by elfedit_next_optarg(). This routine handles the details of 743*5088Sab196087 * knowing whether the string is provided by elfedit itself (inherited), 744*5088Sab196087 * or needs to be translated by the module. 745*5088Sab196087 */ 746*5088Sab196087 const char * 747*5088Sab196087 elfedit_optarg_helpstr(elfeditGC_module_t *mod, elfedit_optarg_item_t *item) 748*5088Sab196087 { 749*5088Sab196087 /* 750*5088Sab196087 * The help string from an inherited item comes right out 751*5088Sab196087 * of the main elfedit string table. 752*5088Sab196087 */ 753*5088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_INHERIT) 754*5088Sab196087 return (MSG_INTL((Msg) item->oai_help)); 755*5088Sab196087 756*5088Sab196087 /* 757*5088Sab196087 * If the string is defined by the module, then we need to 758*5088Sab196087 * have the module translate it for us. 759*5088Sab196087 */ 760*5088Sab196087 return ((* mod->mod_i18nhdl_to_str)(item->oai_help)); 761*5088Sab196087 } 762*5088Sab196087 763*5088Sab196087 764*5088Sab196087 765*5088Sab196087 /* 766*5088Sab196087 * Used by usage_optarg() to insert a character into the output buffer, 767*5088Sab196087 * advancing the buffer pointer and current column, and reducing the 768*5088Sab196087 * amount of remaining space. 769*5088Sab196087 */ 770*5088Sab196087 static void 771*5088Sab196087 usage_optarg_insert_ch(int ch, char **cur, size_t *n, size_t *cur_col) 772*5088Sab196087 { 773*5088Sab196087 774*5088Sab196087 *(*cur)++ = ch; 775*5088Sab196087 **cur = '\0'; 776*5088Sab196087 (*n)--; 777*5088Sab196087 (*cur_col)++; 778*5088Sab196087 } 779*5088Sab196087 780*5088Sab196087 /* 781*5088Sab196087 * Used by usage_optarg() to insert a string into the output 782*5088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing 783*5088Sab196087 * the amount of remaining space. 784*5088Sab196087 */ 785*5088Sab196087 static void 786*5088Sab196087 usage_optarg_insert_str(char **cur, size_t *n, size_t *cur_col, 787*5088Sab196087 const char *format, ...) 788*5088Sab196087 { 789*5088Sab196087 size_t len; 790*5088Sab196087 va_list args; 791*5088Sab196087 792*5088Sab196087 va_start(args, format); 793*5088Sab196087 len = vsnprintf(*cur, *n, format, args); 794*5088Sab196087 va_end(args); 795*5088Sab196087 796*5088Sab196087 *cur += len; 797*5088Sab196087 *n -= len; 798*5088Sab196087 *cur_col += len; 799*5088Sab196087 } 800*5088Sab196087 /* 801*5088Sab196087 * Used by usage_optarg() to insert an optarg item string into the output 802*5088Sab196087 * buffer, advancing the buffer pointer and current column, and reducing 803*5088Sab196087 * the amount of remaining space. 804*5088Sab196087 */ 805*5088Sab196087 static void 806*5088Sab196087 usage_optarg_insert_item(elfedit_optarg_item_t *item, char **cur, 807*5088Sab196087 size_t *n, size_t *cur_col) 808*5088Sab196087 { 809*5088Sab196087 size_t len; 810*5088Sab196087 811*5088Sab196087 if (item->oai_flags & ELFEDIT_CMDOA_F_VALUE) { 812*5088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG2), 813*5088Sab196087 item->oai_name, item->oai_vname); 814*5088Sab196087 } else { 815*5088Sab196087 len = snprintf(*cur, *n, MSG_ORIG(MSG_STR_HLPOPTARG), 816*5088Sab196087 item->oai_name); 817*5088Sab196087 } 818*5088Sab196087 *cur += len; 819*5088Sab196087 *n -= len; 820*5088Sab196087 *cur_col += len; 821*5088Sab196087 } 822*5088Sab196087 823*5088Sab196087 824*5088Sab196087 825*5088Sab196087 /* 826*5088Sab196087 * Write the options/arguments to the usage string. 827*5088Sab196087 * 828*5088Sab196087 * entry: 829*5088Sab196087 * main_buf_n - Size of main buffer from which buf and buf_n are 830*5088Sab196087 * allocated. 831*5088Sab196087 * buf - Address of pointer to where next item is to be placed. 832*5088Sab196087 * buf_n - Address of count of remaining bytes in buffer 833*5088Sab196087 * buf_cur_col - Address of current output column for current line 834*5088Sab196087 * of generated string. 835*5088Sab196087 * optarg - Options list 836*5088Sab196087 * isopt - True if these are options, false for arguments. 837*5088Sab196087 * wrap_str - String to indent wrapped lines. If NULL, lines 838*5088Sab196087 * are not wrapped 839*5088Sab196087 */ 840*5088Sab196087 static void 841*5088Sab196087 usage_optarg(size_t main_buf_n, char **buf, size_t *buf_n, size_t *buf_cur_col, 842*5088Sab196087 elfedit_cmd_optarg_t *optarg, int isopt, const char *wrap_str) 843*5088Sab196087 { 844*5088Sab196087 /* 845*5088Sab196087 * An option can be combined into a simple format if it lacks 846*5088Sab196087 * these flags and is only one character in length. 847*5088Sab196087 */ 848*5088Sab196087 static const elfedit_cmd_oa_flag_t exflags = 849*5088Sab196087 (ELFEDIT_CMDOA_F_VALUE | ELFEDIT_CMDOA_F_MULT); 850*5088Sab196087 851*5088Sab196087 /* 852*5088Sab196087 * A static buffer, which is grown as needed to accomodate 853*5088Sab196087 * the maximum usage string seen. 854*5088Sab196087 */ 855*5088Sab196087 static STRBUF simple_str; 856*5088Sab196087 857*5088Sab196087 char *cur = *buf; 858*5088Sab196087 size_t n = *buf_n; 859*5088Sab196087 size_t cur_col = *buf_cur_col; 860*5088Sab196087 int len; 861*5088Sab196087 int use_simple = 0; 862*5088Sab196087 elfedit_optarg_item_t item; 863*5088Sab196087 elfedit_cmd_oa_mask_t optmask = 0; 864*5088Sab196087 int use_bkt; 865*5088Sab196087 866*5088Sab196087 /* 867*5088Sab196087 * If processing options, pull the 1-character ones that don't have 868*5088Sab196087 * an associated value and don't have any mutual exclusion issues into 869*5088Sab196087 * a single combination string to go at the beginning of the usage. 870*5088Sab196087 */ 871*5088Sab196087 if (isopt) { 872*5088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg; 873*5088Sab196087 char *s; 874*5088Sab196087 875*5088Sab196087 /* 876*5088Sab196087 * The simple string is guaranteed to fit in the same 877*5088Sab196087 * amount of space reserved for the main buffer. 878*5088Sab196087 */ 879*5088Sab196087 strbuf_ensure_size(&simple_str, main_buf_n); 880*5088Sab196087 s = simple_str.buf; 881*5088Sab196087 *s++ = ' '; 882*5088Sab196087 *s++ = '['; 883*5088Sab196087 *s++ = '-'; 884*5088Sab196087 while (tmp_optarg->oa_name != NULL) { 885*5088Sab196087 elfedit_next_optarg(&tmp_optarg, &item); 886*5088Sab196087 if (((item.oai_flags & exflags) == 0) && 887*5088Sab196087 (item.oai_name[2] == '\0') && 888*5088Sab196087 (item.oai_excmask == 0)) { 889*5088Sab196087 optmask |= item.oai_idmask; 890*5088Sab196087 *s++ = item.oai_name[1]; 891*5088Sab196087 } 892*5088Sab196087 } 893*5088Sab196087 894*5088Sab196087 /* 895*5088Sab196087 * If we found more than one, then finish the string and 896*5088Sab196087 * add it. Don't do this for a single option, because 897*5088Sab196087 * it looks better in that case if the option shows up 898*5088Sab196087 * in alphabetical order rather than being hoisted. 899*5088Sab196087 */ 900*5088Sab196087 use_simple = (s > (simple_str.buf + 4)); 901*5088Sab196087 if (use_simple) { 902*5088Sab196087 *s++ = ']'; 903*5088Sab196087 *s++ = '\0'; 904*5088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 905*5088Sab196087 MSG_ORIG(MSG_STR_HLPOPTARG), simple_str.buf); 906*5088Sab196087 } else { 907*5088Sab196087 /* Not using it, so reset the cumulative options mask */ 908*5088Sab196087 optmask = 0; 909*5088Sab196087 } 910*5088Sab196087 } 911*5088Sab196087 912*5088Sab196087 while (optarg->oa_name != NULL) { 913*5088Sab196087 elfedit_next_optarg(&optarg, &item); 914*5088Sab196087 915*5088Sab196087 if (isopt) { 916*5088Sab196087 /* 917*5088Sab196087 * If this is an option that was pulled into the 918*5088Sab196087 * combination string above, then skip over it. 919*5088Sab196087 */ 920*5088Sab196087 if (use_simple && ((item.oai_flags & exflags) == 0) && 921*5088Sab196087 (item.oai_name[2] == '\0') && 922*5088Sab196087 (item.oai_excmask == 0)) 923*5088Sab196087 continue; 924*5088Sab196087 925*5088Sab196087 /* 926*5088Sab196087 * If this is a mutual exclusion option that was 927*5088Sab196087 * picked up out of order by a previous iteration 928*5088Sab196087 * of this loop, then skip over it. 929*5088Sab196087 */ 930*5088Sab196087 if ((optmask & item.oai_idmask) != 0) 931*5088Sab196087 continue; 932*5088Sab196087 933*5088Sab196087 /* Add this item to the accumulating options mask */ 934*5088Sab196087 optmask |= item.oai_idmask; 935*5088Sab196087 } 936*5088Sab196087 937*5088Sab196087 /* Wrap line, or insert blank separator */ 938*5088Sab196087 if ((wrap_str != NULL) && (cur_col > USAGE_WRAP_COL)) { 939*5088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_WRAPUSAGE), 940*5088Sab196087 wrap_str); 941*5088Sab196087 cur += len; 942*5088Sab196087 n -= len; 943*5088Sab196087 cur_col = len - 1; /* Don't count the newline */ 944*5088Sab196087 } else { 945*5088Sab196087 usage_optarg_insert_ch(' ', &cur, &n, &cur_col); 946*5088Sab196087 } 947*5088Sab196087 948*5088Sab196087 use_bkt = (item.oai_flags & ELFEDIT_CMDOA_F_OPT) || isopt; 949*5088Sab196087 if (use_bkt) 950*5088Sab196087 usage_optarg_insert_ch('[', &cur, &n, &cur_col); 951*5088Sab196087 952*5088Sab196087 /* Add the item to the buffer */ 953*5088Sab196087 usage_optarg_insert_item(&item, &cur, &n, &cur_col); 954*5088Sab196087 955*5088Sab196087 /* 956*5088Sab196087 * If this item has a non-zero mutual exclusion mask, 957*5088Sab196087 * then look for the other items and display them all 958*5088Sab196087 * together with alternation (|). Note that plain arguments 959*5088Sab196087 * cannot have a non-0 exclusion mask, so this is 960*5088Sab196087 * effectively options-only (isopt != 0). 961*5088Sab196087 */ 962*5088Sab196087 if (item.oai_excmask != 0) { 963*5088Sab196087 elfedit_cmd_optarg_t *tmp_optarg = optarg; 964*5088Sab196087 elfedit_optarg_item_t tmp_item; 965*5088Sab196087 966*5088Sab196087 /* 967*5088Sab196087 * When showing alternation, elipses for multiple 968*5088Sab196087 * copies need to appear inside the [] brackets. 969*5088Sab196087 */ 970*5088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_MULT) 971*5088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 972*5088Sab196087 MSG_ORIG(MSG_STR_ELIPSES)); 973*5088Sab196087 974*5088Sab196087 975*5088Sab196087 while (tmp_optarg->oa_name != NULL) { 976*5088Sab196087 elfedit_next_optarg(&tmp_optarg, &tmp_item); 977*5088Sab196087 if ((item.oai_excmask & tmp_item.oai_idmask) == 978*5088Sab196087 0) 979*5088Sab196087 continue; 980*5088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 981*5088Sab196087 MSG_ORIG(MSG_STR_SP_BAR_SP)); 982*5088Sab196087 usage_optarg_insert_item(&tmp_item, 983*5088Sab196087 &cur, &n, &cur_col); 984*5088Sab196087 985*5088Sab196087 /* 986*5088Sab196087 * Add it to the mask of seen options. 987*5088Sab196087 * This will keep us from showing it twice. 988*5088Sab196087 */ 989*5088Sab196087 optmask |= tmp_item.oai_idmask; 990*5088Sab196087 } 991*5088Sab196087 } 992*5088Sab196087 if (use_bkt) 993*5088Sab196087 usage_optarg_insert_ch(']', &cur, &n, &cur_col); 994*5088Sab196087 995*5088Sab196087 /* 996*5088Sab196087 * If alternation was not shown above (non-zero exclusion mask) 997*5088Sab196087 * then the elipses for multiple copies are shown outside 998*5088Sab196087 * any [] brackets. 999*5088Sab196087 */ 1000*5088Sab196087 if ((item.oai_excmask == 0) && 1001*5088Sab196087 (item.oai_flags & ELFEDIT_CMDOA_F_MULT)) 1002*5088Sab196087 usage_optarg_insert_str(&cur, &n, &cur_col, 1003*5088Sab196087 MSG_ORIG(MSG_STR_ELIPSES)); 1004*5088Sab196087 1005*5088Sab196087 } 1006*5088Sab196087 1007*5088Sab196087 *buf = cur; 1008*5088Sab196087 *buf_n = n; 1009*5088Sab196087 *buf_cur_col = cur_col; 1010*5088Sab196087 } 1011*5088Sab196087 1012*5088Sab196087 1013*5088Sab196087 1014*5088Sab196087 /* 1015*5088Sab196087 * Format the usage string for a command into a static buffer and 1016*5088Sab196087 * return the pointer to the user. The resultant string is valid 1017*5088Sab196087 * until the next call to this routine, and which point it 1018*5088Sab196087 * will be overwritten or the memory is freed. 1019*5088Sab196087 * 1020*5088Sab196087 * entry: 1021*5088Sab196087 * mod, cmd - Module and command definitions for command to be described 1022*5088Sab196087 * wrap_str - NULL, or string to be used to indent when 1023*5088Sab196087 * lines are wrapped. If NULL, no wrapping is done, and 1024*5088Sab196087 * all output is on a single line. 1025*5088Sab196087 * cur_col - Starting column at which the string will be displayed. 1026*5088Sab196087 * Ignored if wrap_str is NULL. 1027*5088Sab196087 */ 1028*5088Sab196087 const char * 1029*5088Sab196087 elfedit_format_command_usage(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd, 1030*5088Sab196087 const char *wrap_str, size_t cur_col) 1031*5088Sab196087 { 1032*5088Sab196087 1033*5088Sab196087 /* 1034*5088Sab196087 * A static buffer, which is grown as needed to accomodate 1035*5088Sab196087 * the maximum usage string seen. 1036*5088Sab196087 */ 1037*5088Sab196087 static STRBUF str; 1038*5088Sab196087 1039*5088Sab196087 elfedit_cmd_optarg_t *optarg; 1040*5088Sab196087 size_t len, n, elipses_len; 1041*5088Sab196087 char *cur; 1042*5088Sab196087 elfedit_optarg_item_t item; 1043*5088Sab196087 1044*5088Sab196087 /* 1045*5088Sab196087 * Estimate a worst case size for the usage string: 1046*5088Sab196087 * - module name 1047*5088Sab196087 * - lengths of the strings 1048*5088Sab196087 * - every option or argument is enclosed in brackets 1049*5088Sab196087 * - space in between each item, with an alternation (" | ") 1050*5088Sab196087 * - elipses will be displayed with each option and argument 1051*5088Sab196087 */ 1052*5088Sab196087 n = strlen(mod->mod_name) + strlen(cmd->cmd_name[0]) + 6; 1053*5088Sab196087 elipses_len = strlen(MSG_ORIG(MSG_STR_ELIPSES)); 1054*5088Sab196087 if ((optarg = cmd->cmd_opt) != NULL) 1055*5088Sab196087 while (optarg->oa_name != NULL) { 1056*5088Sab196087 elfedit_next_optarg(&optarg, &item); 1057*5088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len; 1058*5088Sab196087 } 1059*5088Sab196087 if ((optarg = cmd->cmd_args) != NULL) 1060*5088Sab196087 while (optarg->oa_name != NULL) { 1061*5088Sab196087 elfedit_next_optarg(&optarg, &item); 1062*5088Sab196087 n += strlen(item.oai_name) + 5 + elipses_len; 1063*5088Sab196087 } 1064*5088Sab196087 n++; /* Null termination */ 1065*5088Sab196087 1066*5088Sab196087 /* 1067*5088Sab196087 * If wrapping lines, we insert a newline and then wrap_str 1068*5088Sab196087 * every USAGE_WRAP_COL characters. 1069*5088Sab196087 */ 1070*5088Sab196087 if (wrap_str != NULL) 1071*5088Sab196087 n += ((n + USAGE_WRAP_COL) / USAGE_WRAP_COL) * 1072*5088Sab196087 (strlen(wrap_str) + 1); 1073*5088Sab196087 1074*5088Sab196087 strbuf_ensure_size(&str, n); 1075*5088Sab196087 1076*5088Sab196087 /* Command name */ 1077*5088Sab196087 cur = str.buf; 1078*5088Sab196087 n = str.n; 1079*5088Sab196087 if (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) == 0) 1080*5088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_SYSCMD), 1081*5088Sab196087 cmd->cmd_name[0]); 1082*5088Sab196087 else 1083*5088Sab196087 len = snprintf(cur, n, MSG_ORIG(MSG_FMT_MODCMD), 1084*5088Sab196087 mod->mod_name, cmd->cmd_name[0]); 1085*5088Sab196087 cur += len; 1086*5088Sab196087 n -= len; 1087*5088Sab196087 cur_col += len; 1088*5088Sab196087 1089*5088Sab196087 if (cmd->cmd_opt != NULL) 1090*5088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_opt, 1091*5088Sab196087 1, wrap_str); 1092*5088Sab196087 if (cmd->cmd_args != NULL) 1093*5088Sab196087 usage_optarg(str.n, &cur, &n, &cur_col, cmd->cmd_args, 1094*5088Sab196087 0, wrap_str); 1095*5088Sab196087 1096*5088Sab196087 return (str.buf); 1097*5088Sab196087 } 1098*5088Sab196087 1099*5088Sab196087 /* 1100*5088Sab196087 * Wrapper on elfedit_msg() that issues an ELFEDIT_MSG_USAGE 1101*5088Sab196087 * error giving usage information for the command currently 1102*5088Sab196087 * referenced by state.cur_cmd. 1103*5088Sab196087 */ 1104*5088Sab196087 void 1105*5088Sab196087 elfedit_command_usage(void) 1106*5088Sab196087 { 1107*5088Sab196087 elfedit_msg(ELFEDIT_MSG_CMDUSAGE, MSG_INTL(MSG_USAGE_CMD), 1108*5088Sab196087 elfedit_format_command_usage(state.cur_cmd->ucmd_mod, 1109*5088Sab196087 state.cur_cmd->ucmd_cmd, NULL, 0)); 1110*5088Sab196087 } 1111*5088Sab196087 1112*5088Sab196087 1113*5088Sab196087 /* 1114*5088Sab196087 * This function allows the loadable modules to get the command line 1115*5088Sab196087 * flags. 1116*5088Sab196087 */ 1117*5088Sab196087 elfedit_flag_t 1118*5088Sab196087 elfedit_flags(void) 1119*5088Sab196087 { 1120*5088Sab196087 return (state.flags); 1121*5088Sab196087 } 1122*5088Sab196087 1123*5088Sab196087 /* 1124*5088Sab196087 * This function is used to register a per-command invocation output style 1125*5088Sab196087 * that will momentarily override the global output style for the duration 1126*5088Sab196087 * of the current command. This function must only be called by an 1127*5088Sab196087 * active command. 1128*5088Sab196087 * 1129*5088Sab196087 * entry: 1130*5088Sab196087 * str - One of the valid strings for the output style 1131*5088Sab196087 */ 1132*5088Sab196087 void 1133*5088Sab196087 elfedit_set_cmd_outstyle(const char *str) 1134*5088Sab196087 { 1135*5088Sab196087 if ((state.cur_cmd != NULL) && (str != NULL)) { 1136*5088Sab196087 if (elfedit_atooutstyle(str, &state.cur_cmd->ucmd_ostyle) == 0) 1137*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 1138*5088Sab196087 MSG_INTL(MSG_ERR_BADOSTYLE), str); 1139*5088Sab196087 state.cur_cmd->ucmd_ostyle_set = 1; 1140*5088Sab196087 } 1141*5088Sab196087 } 1142*5088Sab196087 1143*5088Sab196087 /* 1144*5088Sab196087 * This function allows the loadable modules to get the output style. 1145*5088Sab196087 */ 1146*5088Sab196087 elfedit_outstyle_t 1147*5088Sab196087 elfedit_outstyle(void) 1148*5088Sab196087 { 1149*5088Sab196087 /* 1150*5088Sab196087 * If there is an active per-command output style, 1151*5088Sab196087 * return it. 1152*5088Sab196087 */ 1153*5088Sab196087 if ((state.cur_cmd != NULL) && (state.cur_cmd->ucmd_ostyle_set)) 1154*5088Sab196087 return (state.cur_cmd->ucmd_ostyle); 1155*5088Sab196087 1156*5088Sab196087 1157*5088Sab196087 return (state.outstyle); 1158*5088Sab196087 } 1159*5088Sab196087 1160*5088Sab196087 /* 1161*5088Sab196087 * Return the command descriptor of the currently executing command. 1162*5088Sab196087 * For use only by the modules or code called by the modules. 1163*5088Sab196087 */ 1164*5088Sab196087 elfeditGC_cmd_t * 1165*5088Sab196087 elfedit_curcmd(void) 1166*5088Sab196087 { 1167*5088Sab196087 return (state.cur_cmd->ucmd_cmd); 1168*5088Sab196087 } 1169*5088Sab196087 1170*5088Sab196087 /* 1171*5088Sab196087 * Build a dynamically allocated elfedit_obj_state_t struct that 1172*5088Sab196087 * contains a cache of the ELF file contents. This pre-chewed form 1173*5088Sab196087 * is fed to each command, reducing the amount of ELF boilerplate 1174*5088Sab196087 * code each command needs to contain. 1175*5088Sab196087 * 1176*5088Sab196087 * entry: 1177*5088Sab196087 * file - Name of file to process 1178*5088Sab196087 * 1179*5088Sab196087 * exit: 1180*5088Sab196087 * Fills state.elf with the necessary information for the open file. 1181*5088Sab196087 * 1182*5088Sab196087 * note: The resulting elfedit_obj_state_t is allocated from a single 1183*5088Sab196087 * piece of memory, such that a single call to free() suffices 1184*5088Sab196087 * to release it as well as any memory it references. 1185*5088Sab196087 */ 1186*5088Sab196087 static void 1187*5088Sab196087 init_obj_state(const char *file) 1188*5088Sab196087 { 1189*5088Sab196087 int fd; 1190*5088Sab196087 Elf *elf; 1191*5088Sab196087 int open_flag; 1192*5088Sab196087 1193*5088Sab196087 /* 1194*5088Sab196087 * In readonly mode, we open the file readonly so that it is 1195*5088Sab196087 * impossible to modify the file by accident. This also allows 1196*5088Sab196087 * us to access readonly files, perhaps in a case where we don't 1197*5088Sab196087 * intend to change it. 1198*5088Sab196087 * 1199*5088Sab196087 * We always use ELF_C_RDWR with elf_begin(), even in a readonly 1200*5088Sab196087 * session. This allows us to modify the in-memory image, which 1201*5088Sab196087 * can be useful when examining a file, even though we don't intend 1202*5088Sab196087 * to modify the on-disk data. The file is not writable in 1203*5088Sab196087 * this case, and we don't call elf_update(), so it is safe to do so. 1204*5088Sab196087 */ 1205*5088Sab196087 open_flag = ((state.flags & ELFEDIT_F_READONLY) ? O_RDONLY : O_RDWR); 1206*5088Sab196087 if ((fd = open(file, open_flag)) == -1) { 1207*5088Sab196087 int err = errno; 1208*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNFILE), 1209*5088Sab196087 file, strerror(err)); 1210*5088Sab196087 } 1211*5088Sab196087 (void) elf_version(EV_CURRENT); 1212*5088Sab196087 elf = elf_begin(fd, ELF_C_RDWR, NULL); 1213*5088Sab196087 if (elf == NULL) { 1214*5088Sab196087 (void) close(fd); 1215*5088Sab196087 elfedit_elferr(file, MSG_ORIG(MSG_ELF_BEGIN)); 1216*5088Sab196087 /*NOTREACHED*/ 1217*5088Sab196087 } 1218*5088Sab196087 1219*5088Sab196087 /* We only handle standalone ELF files */ 1220*5088Sab196087 switch (elf_kind(elf)) { 1221*5088Sab196087 case ELF_K_AR: 1222*5088Sab196087 (void) close(fd); 1223*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOAR), file); 1224*5088Sab196087 break; 1225*5088Sab196087 case ELF_K_ELF: 1226*5088Sab196087 break; 1227*5088Sab196087 default: 1228*5088Sab196087 (void) close(fd); 1229*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_UNRECELFFILE), 1230*5088Sab196087 file); 1231*5088Sab196087 break; 1232*5088Sab196087 } 1233*5088Sab196087 1234*5088Sab196087 /* 1235*5088Sab196087 * Tell libelf that we take responsibility for object layout. 1236*5088Sab196087 * Otherwise, it will compute "proper" values for layout and 1237*5088Sab196087 * alignment fields, and these values can overwrite the values 1238*5088Sab196087 * set in the elfedit session. We are modifying existing 1239*5088Sab196087 * objects --- the layout concerns have already been dealt 1240*5088Sab196087 * with when the object was built. 1241*5088Sab196087 */ 1242*5088Sab196087 (void) elf_flagelf(elf, ELF_C_SET, ELF_F_LAYOUT); 1243*5088Sab196087 1244*5088Sab196087 /* Fill in state.elf.obj_state */ 1245*5088Sab196087 state.elf.elfclass = gelf_getclass(elf); 1246*5088Sab196087 switch (state.elf.elfclass) { 1247*5088Sab196087 case ELFCLASS32: 1248*5088Sab196087 elfedit32_init_obj_state(file, fd, elf); 1249*5088Sab196087 break; 1250*5088Sab196087 case ELFCLASS64: 1251*5088Sab196087 elfedit64_init_obj_state(file, fd, elf); 1252*5088Sab196087 break; 1253*5088Sab196087 default: 1254*5088Sab196087 (void) close(fd); 1255*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADELFCLASS), 1256*5088Sab196087 file); 1257*5088Sab196087 break; 1258*5088Sab196087 } 1259*5088Sab196087 } 1260*5088Sab196087 1261*5088Sab196087 1262*5088Sab196087 #if 0 1263*5088Sab196087 /* 1264*5088Sab196087 * Debug routine. Dump the module list to stdout. 1265*5088Sab196087 */ 1266*5088Sab196087 static void 1267*5088Sab196087 dbg_module_list(char *title) 1268*5088Sab196087 { 1269*5088Sab196087 MODLIST_T *m; 1270*5088Sab196087 1271*5088Sab196087 printf("<MODULE LIST: %s>\n", title); 1272*5088Sab196087 for (m = state.modlist; m != NULL; m = m->next) { 1273*5088Sab196087 printf("Module: >%s<\n", m->mod->mod_name); 1274*5088Sab196087 printf(" hdl: %llx\n", m->dl_hdl); 1275*5088Sab196087 printf(" path: >%s<\n", m->path ? m->path : "<builtin>"); 1276*5088Sab196087 } 1277*5088Sab196087 printf("<END OF MODULE LIST>\n"); 1278*5088Sab196087 } 1279*5088Sab196087 #endif 1280*5088Sab196087 1281*5088Sab196087 1282*5088Sab196087 /* 1283*5088Sab196087 * Search the module list for the named module. 1284*5088Sab196087 * 1285*5088Sab196087 * entry: 1286*5088Sab196087 * name - Name of module to find 1287*5088Sab196087 * insdef - Address of variable to receive address of predecessor 1288*5088Sab196087 * node to the desired one. 1289*5088Sab196087 * 1290*5088Sab196087 * exit: 1291*5088Sab196087 * If the module is it is found, this routine returns the pointer to 1292*5088Sab196087 * its MODLIST_T structure. *insdef references the predecessor node, or 1293*5088Sab196087 * is NULL if the found item is at the head of the list. 1294*5088Sab196087 * 1295*5088Sab196087 * If the module is not found, NULL is returned. *insdef references 1296*5088Sab196087 * the predecessor node of the position where an entry for this module 1297*5088Sab196087 * would be placed, or NULL if it would go at the beginning. 1298*5088Sab196087 */ 1299*5088Sab196087 static MODLIST_T * 1300*5088Sab196087 module_loaded(const char *name, MODLIST_T **insdef) 1301*5088Sab196087 { 1302*5088Sab196087 MODLIST_T *moddef; 1303*5088Sab196087 int cmp; 1304*5088Sab196087 1305*5088Sab196087 *insdef = NULL; 1306*5088Sab196087 moddef = state.modlist; 1307*5088Sab196087 if (moddef != NULL) { 1308*5088Sab196087 cmp = strcasecmp(name, moddef->ml_mod->mod_name); 1309*5088Sab196087 if (cmp == 0) { /* Desired module is first in list */ 1310*5088Sab196087 return (moddef); 1311*5088Sab196087 } else if (cmp > 0) { /* cmp > 0: Insert in middle/end */ 1312*5088Sab196087 *insdef = moddef; 1313*5088Sab196087 moddef = moddef->ml_next; 1314*5088Sab196087 cmp = -1; 1315*5088Sab196087 while (moddef && (cmp < 0)) { 1316*5088Sab196087 cmp = strcasecmp(moddef->ml_mod->mod_name, 1317*5088Sab196087 name); 1318*5088Sab196087 if (cmp == 0) 1319*5088Sab196087 return (moddef); 1320*5088Sab196087 if (cmp < 0) { 1321*5088Sab196087 *insdef = moddef; 1322*5088Sab196087 moddef = (*insdef)->ml_next; 1323*5088Sab196087 } 1324*5088Sab196087 } 1325*5088Sab196087 } 1326*5088Sab196087 } 1327*5088Sab196087 1328*5088Sab196087 return (NULL); 1329*5088Sab196087 } 1330*5088Sab196087 1331*5088Sab196087 1332*5088Sab196087 /* 1333*5088Sab196087 * Determine if a file is a sharable object based on its file path. 1334*5088Sab196087 * If path ends in a .so, followed optionally by a period and 1 or more 1335*5088Sab196087 * digits, we say that it is and return a pointer to the first character 1336*5088Sab196087 * of the suffix. Otherwise NULL is returned. 1337*5088Sab196087 */ 1338*5088Sab196087 static const char * 1339*5088Sab196087 path_is_so(const char *path) 1340*5088Sab196087 { 1341*5088Sab196087 int dotso_len; 1342*5088Sab196087 const char *tail; 1343*5088Sab196087 size_t len; 1344*5088Sab196087 1345*5088Sab196087 len = strlen(path); 1346*5088Sab196087 if (len == 0) 1347*5088Sab196087 return (NULL); 1348*5088Sab196087 tail = path + len; 1349*5088Sab196087 if (isdigit(*(tail - 1))) { 1350*5088Sab196087 while ((tail > path) && isdigit(*(tail - 1))) 1351*5088Sab196087 tail--; 1352*5088Sab196087 if ((tail <= path) || (*tail != '.')) 1353*5088Sab196087 return (NULL); 1354*5088Sab196087 } 1355*5088Sab196087 dotso_len = strlen(MSG_ORIG(MSG_STR_DOTSO)); 1356*5088Sab196087 if ((tail - path) < dotso_len) 1357*5088Sab196087 return (NULL); 1358*5088Sab196087 tail -= dotso_len; 1359*5088Sab196087 if (strncmp(tail, MSG_ORIG(MSG_STR_DOTSO), dotso_len) == 0) 1360*5088Sab196087 return (tail); 1361*5088Sab196087 1362*5088Sab196087 return (NULL); 1363*5088Sab196087 } 1364*5088Sab196087 1365*5088Sab196087 1366*5088Sab196087 /* 1367*5088Sab196087 * Locate the start of the unsuffixed file name within path. Returns pointer 1368*5088Sab196087 * to first character of that name in path. 1369*5088Sab196087 * 1370*5088Sab196087 * entry: 1371*5088Sab196087 * path - Path to be examined. 1372*5088Sab196087 * tail - NULL, or pointer to position at tail of path from which 1373*5088Sab196087 * the search for '/' characters should start. If NULL, 1374*5088Sab196087 * strlen() is used to locate the end of the string. 1375*5088Sab196087 * buf - NULL, or buffer to receive a copy of the characters that 1376*5088Sab196087 * lie between the start of the filename and tail. 1377*5088Sab196087 * bufsize - sizeof(buf) 1378*5088Sab196087 * 1379*5088Sab196087 * exit: 1380*5088Sab196087 * The pointer to the first character of the unsuffixed file name 1381*5088Sab196087 * within path is returned. If buf is non-NULL, the characters 1382*5088Sab196087 * lying between that point and tail (or the end of path if tail 1383*5088Sab196087 * is NULL) are copied into buf. 1384*5088Sab196087 */ 1385*5088Sab196087 static const char * 1386*5088Sab196087 elfedit_basename(const char *path, const char *tail, char *buf, size_t bufsiz) 1387*5088Sab196087 { 1388*5088Sab196087 const char *s; 1389*5088Sab196087 1390*5088Sab196087 if (tail == NULL) 1391*5088Sab196087 tail = path + strlen(path); 1392*5088Sab196087 s = tail; 1393*5088Sab196087 while ((s > path) && (*(s - 1) != '/')) 1394*5088Sab196087 s--; 1395*5088Sab196087 if (buf != NULL) 1396*5088Sab196087 elfedit_strnbcpy(buf, s, tail - s, bufsiz); 1397*5088Sab196087 return (s); 1398*5088Sab196087 } 1399*5088Sab196087 1400*5088Sab196087 1401*5088Sab196087 /* 1402*5088Sab196087 * Issue an error on behalf of load_module(), taking care to release 1403*5088Sab196087 * resources that routine may have aquired: 1404*5088Sab196087 * 1405*5088Sab196087 * entry: 1406*5088Sab196087 * moddef - NULL, or a module definition to be released via free() 1407*5088Sab196087 * dl_hdl - NULL, or a handle to a sharable object to release via 1408*5088Sab196087 * dlclose(). 1409*5088Sab196087 * dl_path - If dl_hdl is non-NULL, the path to the sharable object 1410*5088Sab196087 * file that was loaded. 1411*5088Sab196087 * format - A format string to pass to elfedit_msg(), containing 1412*5088Sab196087 * no more than (3) %s format codes, and no other format codes. 1413*5088Sab196087 * [s1-s4] - Strings to pass to elfedit_msg() to satisfy the four 1414*5088Sab196087 * allowed %s codes in format. Should be set to NULL if the 1415*5088Sab196087 * format string does not need them. 1416*5088Sab196087 * 1417*5088Sab196087 * note: 1418*5088Sab196087 * This routine makes a copy of the s1-s4 strings before freeing any 1419*5088Sab196087 * memory or unmapping the sharable library. It is therefore safe to 1420*5088Sab196087 * use strings from moddef, or from the sharable library (which will 1421*5088Sab196087 * be unmapped) to satisfy the other arguments s1-s4. 1422*5088Sab196087 */ 1423*5088Sab196087 static void 1424*5088Sab196087 load_module_err(MODLIST_T *moddef, void *dl_hdl, const char *dl_path, 1425*5088Sab196087 const char *format, const char *s1, const char *s2, const char *s3, 1426*5088Sab196087 const char *s4) 1427*5088Sab196087 { 1428*5088Sab196087 #define SCRBUFSIZE (PATH_MAX + 256) /* A path, plus some extra */ 1429*5088Sab196087 1430*5088Sab196087 char s1_buf[SCRBUFSIZE]; 1431*5088Sab196087 char s2_buf[SCRBUFSIZE]; 1432*5088Sab196087 char s3_buf[SCRBUFSIZE]; 1433*5088Sab196087 char s4_buf[SCRBUFSIZE]; 1434*5088Sab196087 1435*5088Sab196087 /* 1436*5088Sab196087 * The caller may provide strings for s1-s3 that are from 1437*5088Sab196087 * moddef. If we free moddef, the printf() will die on access 1438*5088Sab196087 * to free memory. We could push back on the user and force 1439*5088Sab196087 * each call to carefully make copies of such data. However, this 1440*5088Sab196087 * is an easy case to miss. Furthermore, this is an error case, 1441*5088Sab196087 * and machine efficiency is not the main issue. We therefore make 1442*5088Sab196087 * copies of the s1-s3 strings here into auto variables, and then 1443*5088Sab196087 * use those copies. The user is freed from worrying about it. 1444*5088Sab196087 * 1445*5088Sab196087 * We use oversized stack based buffers instead of malloc() to 1446*5088Sab196087 * reduce the number of ways that things can go wrong while 1447*5088Sab196087 * reporting the error. 1448*5088Sab196087 */ 1449*5088Sab196087 if (s1 != NULL) 1450*5088Sab196087 (void) strlcpy(s1_buf, s1, sizeof (s1_buf)); 1451*5088Sab196087 if (s2 != NULL) 1452*5088Sab196087 (void) strlcpy(s2_buf, s2, sizeof (s2_buf)); 1453*5088Sab196087 if (s3 != NULL) 1454*5088Sab196087 (void) strlcpy(s3_buf, s3, sizeof (s3_buf)); 1455*5088Sab196087 if (s4 != NULL) 1456*5088Sab196087 (void) strlcpy(s4_buf, s4, sizeof (s4_buf)); 1457*5088Sab196087 1458*5088Sab196087 1459*5088Sab196087 if (moddef != NULL) 1460*5088Sab196087 free(moddef); 1461*5088Sab196087 1462*5088Sab196087 if ((dl_hdl != NULL) && (dlclose(dl_hdl) != 0)) 1463*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE), 1464*5088Sab196087 dl_path, dlerror()); 1465*5088Sab196087 1466*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, format, s1_buf, s2_buf, s3_buf, s4_buf); 1467*5088Sab196087 #undef SCRBUFSIZE 1468*5088Sab196087 } 1469*5088Sab196087 1470*5088Sab196087 1471*5088Sab196087 /* 1472*5088Sab196087 * Load a module sharable object for load_module(). 1473*5088Sab196087 * 1474*5088Sab196087 * entry: 1475*5088Sab196087 * path - Path of file to open 1476*5088Sab196087 * moddef - If this function issues a non-returning error, it will 1477*5088Sab196087 * first return the memory referenced by moddef. This argument 1478*5088Sab196087 * is not used otherwise. 1479*5088Sab196087 * must_exist - If True, we consider it to be an error if the file given 1480*5088Sab196087 * by path does not exist. If False, no error is issued 1481*5088Sab196087 * and a NULL value is quietly returned. 1482*5088Sab196087 * 1483*5088Sab196087 * exit: 1484*5088Sab196087 * Returns a handle to the loaded object on success, or NULL if no 1485*5088Sab196087 * file was loaded. 1486*5088Sab196087 */ 1487*5088Sab196087 static void * 1488*5088Sab196087 load_module_dlopen(const char *path, MODLIST_T *moddef, int must_exist) 1489*5088Sab196087 { 1490*5088Sab196087 int fd; 1491*5088Sab196087 void *hdl; 1492*5088Sab196087 1493*5088Sab196087 /* 1494*5088Sab196087 * If the file is not required to exist, and it doesn't, then 1495*5088Sab196087 * we want to quietly return without an error. 1496*5088Sab196087 */ 1497*5088Sab196087 if (!must_exist) { 1498*5088Sab196087 fd = open(path, O_RDONLY); 1499*5088Sab196087 if (fd >= 0) { 1500*5088Sab196087 (void) close(fd); 1501*5088Sab196087 } else if (errno == ENOENT) { 1502*5088Sab196087 return (NULL); 1503*5088Sab196087 } 1504*5088Sab196087 } 1505*5088Sab196087 1506*5088Sab196087 if ((hdl = dlopen(path, RTLD_LAZY|RTLD_FIRST)) == NULL) 1507*5088Sab196087 load_module_err(moddef, NULL, NULL, 1508*5088Sab196087 MSG_INTL(MSG_ERR_CNTDLOPEN), path, dlerror(), NULL, NULL); 1509*5088Sab196087 1510*5088Sab196087 return (hdl); 1511*5088Sab196087 } 1512*5088Sab196087 1513*5088Sab196087 1514*5088Sab196087 /* 1515*5088Sab196087 * Sanity check option arguments to prevent common errors. The rest of 1516*5088Sab196087 * elfedit assumes these tests have been done, and does not check 1517*5088Sab196087 * again. 1518*5088Sab196087 */ 1519*5088Sab196087 static void 1520*5088Sab196087 validate_optarg(elfedit_cmd_optarg_t *optarg, int isopt, MODLIST_T *moddef, 1521*5088Sab196087 const char *mod_name, const char *cmd_name, 1522*5088Sab196087 void *dl_hdl, const char *dl_path) 1523*5088Sab196087 { 1524*5088Sab196087 #define FAIL(_msg) errmsg = _msg; goto fail 1525*5088Sab196087 1526*5088Sab196087 Msg errmsg; 1527*5088Sab196087 elfedit_cmd_oa_mask_t optmask = 0; 1528*5088Sab196087 1529*5088Sab196087 for (; optarg->oa_name != NULL; optarg++) { 1530*5088Sab196087 /* 1531*5088Sab196087 * If ELFEDIT_CMDOA_F_INHERIT is set: 1532*5088Sab196087 * - oa_name must be a value in the range of 1533*5088Sab196087 * known ELFEDIT_STDOA_ values. 1534*5088Sab196087 * - oa_help must be NULL 1535*5088Sab196087 * - ELFEDIT_CMDOA_F_INHERIT must be the only flag set 1536*5088Sab196087 */ 1537*5088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) { 1538*5088Sab196087 if ((((uintptr_t)optarg->oa_name) > 1539*5088Sab196087 ELFEDIT_NUM_STDOA) || 1540*5088Sab196087 (optarg->oa_help != 0) || 1541*5088Sab196087 (optarg->oa_flags != ELFEDIT_CMDOA_F_INHERIT)) 1542*5088Sab196087 /* 1543*5088Sab196087 * Can't use FAIL --- oa_name is not a valid 1544*5088Sab196087 * string, and load_module_err() looks at args. 1545*5088Sab196087 */ 1546*5088Sab196087 load_module_err(moddef, dl_hdl, dl_path, 1547*5088Sab196087 MSG_INTL(MSG_ERR_BADSTDOA), dl_path, 1548*5088Sab196087 mod_name, cmd_name, NULL); 1549*5088Sab196087 continue; 1550*5088Sab196087 } 1551*5088Sab196087 1552*5088Sab196087 if (isopt) { 1553*5088Sab196087 /* 1554*5088Sab196087 * Option name must start with a '-', and must 1555*5088Sab196087 * have at one following character. 1556*5088Sab196087 */ 1557*5088Sab196087 if (optarg->oa_name[0] != '-') { 1558*5088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODPRE) */ 1559*5088Sab196087 FAIL(MSG_ERR_OPT_MODPRE); 1560*5088Sab196087 } 1561*5088Sab196087 if (optarg->oa_name[1] == '\0') { 1562*5088Sab196087 /* MSG_INTL(MSG_ERR_OPT_MODLEN) */ 1563*5088Sab196087 FAIL(MSG_ERR_OPT_MODLEN); 1564*5088Sab196087 } 1565*5088Sab196087 1566*5088Sab196087 /* 1567*5088Sab196087 * oa_idmask must be 0, or it must have a single 1568*5088Sab196087 * bit set (a power of 2).oa_excmask must be 0 1569*5088Sab196087 * if oa_idmask is 0 1570*5088Sab196087 */ 1571*5088Sab196087 if (optarg->oa_idmask == 0) { 1572*5088Sab196087 if (optarg->oa_excmask != 0) { 1573*5088Sab196087 /* MSG_INTL(MSG_ERR_OPT_EXCMASKN0) */ 1574*5088Sab196087 FAIL(MSG_ERR_OPT_EXCMASKN0); 1575*5088Sab196087 } 1576*5088Sab196087 } else { 1577*5088Sab196087 if (elfedit_bits_set(optarg->oa_idmask, 1578*5088Sab196087 sizeof (optarg->oa_idmask)) != 1) { 1579*5088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKPOW2) */ 1580*5088Sab196087 FAIL(MSG_ERR_OPT_IDMASKPOW2); 1581*5088Sab196087 } 1582*5088Sab196087 1583*5088Sab196087 /* Non-zero idmask must be unique */ 1584*5088Sab196087 if ((optarg->oa_idmask & optmask) != 0) { 1585*5088Sab196087 /* MSG_INTL(MSG_ERR_OPT_IDMASKUNIQ) */ 1586*5088Sab196087 FAIL(MSG_ERR_OPT_IDMASKUNIQ); 1587*5088Sab196087 } 1588*5088Sab196087 1589*5088Sab196087 /* Add this one to the overall mask */ 1590*5088Sab196087 optmask |= optarg->oa_idmask; 1591*5088Sab196087 } 1592*5088Sab196087 } else { 1593*5088Sab196087 /* 1594*5088Sab196087 * Argument name cannot start with a'-', and must 1595*5088Sab196087 * not be a null string. 1596*5088Sab196087 */ 1597*5088Sab196087 if (optarg->oa_name[0] == '-') { 1598*5088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODPRE) */ 1599*5088Sab196087 FAIL(MSG_ERR_ARG_MODPRE); 1600*5088Sab196087 } 1601*5088Sab196087 if (optarg->oa_name[1] == '\0') { 1602*5088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MODLEN) */ 1603*5088Sab196087 FAIL(MSG_ERR_ARG_MODLEN); 1604*5088Sab196087 } 1605*5088Sab196087 1606*5088Sab196087 1607*5088Sab196087 /* oa_idmask and oa_excmask must both be 0 */ 1608*5088Sab196087 if ((optarg->oa_idmask != 0) || 1609*5088Sab196087 (optarg->oa_excmask != 0)) { 1610*5088Sab196087 /* MSG_INTL(MSG_ERR_ARG_MASKNOT0) */ 1611*5088Sab196087 FAIL(MSG_ERR_ARG_MASKNOT0); 1612*5088Sab196087 } 1613*5088Sab196087 1614*5088Sab196087 } 1615*5088Sab196087 1616*5088Sab196087 /* 1617*5088Sab196087 * If it takes a value, make sure that we are 1618*5088Sab196087 * processing options, because CMDOA_F_VALUE is not 1619*5088Sab196087 * allowed for plain arguments. Then check the following 1620*5088Sab196087 * item in the list: 1621*5088Sab196087 * - There must be a following item. 1622*5088Sab196087 * - oa_name must be non-NULL. This is the only field 1623*5088Sab196087 * that is used by elfedit. 1624*5088Sab196087 * - oa_help, oa_flags, oa_idmask, and oa_excmask 1625*5088Sab196087 * must be 0. 1626*5088Sab196087 */ 1627*5088Sab196087 if (optarg->oa_flags & ELFEDIT_CMDOA_F_VALUE) { 1628*5088Sab196087 elfedit_cmd_optarg_t *oa1 = optarg + 1; 1629*5088Sab196087 1630*5088Sab196087 if (!isopt) { 1631*5088Sab196087 /* MSG_INTL(MSG_ERR_ARG_CMDOA_VAL) */ 1632*5088Sab196087 FAIL(MSG_ERR_ARG_CMDOA_VAL); 1633*5088Sab196087 } 1634*5088Sab196087 1635*5088Sab196087 if ((optarg + 1)->oa_name == NULL) { 1636*5088Sab196087 /* MSG_INTL(MSG_ERR_BADMODOPTVAL) */ 1637*5088Sab196087 FAIL(MSG_ERR_BADMODOPTVAL); 1638*5088Sab196087 } 1639*5088Sab196087 1640*5088Sab196087 if (oa1->oa_name == NULL) { 1641*5088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNAM) */ 1642*5088Sab196087 FAIL(MSG_ERR_CMDOA_VALNAM); 1643*5088Sab196087 } 1644*5088Sab196087 if ((oa1->oa_help != NULL) || (oa1->oa_flags != 0) || 1645*5088Sab196087 (oa1->oa_idmask != 0) || (oa1->oa_excmask != 0)) { 1646*5088Sab196087 /* MSG_INTL(MSG_ERR_CMDOA_VALNOT0) */ 1647*5088Sab196087 FAIL(MSG_ERR_CMDOA_VALNOT0); 1648*5088Sab196087 } 1649*5088Sab196087 optarg++; 1650*5088Sab196087 } 1651*5088Sab196087 } 1652*5088Sab196087 1653*5088Sab196087 1654*5088Sab196087 return; 1655*5088Sab196087 1656*5088Sab196087 fail: 1657*5088Sab196087 load_module_err(moddef, dl_hdl, dl_path, MSG_INTL(errmsg), 1658*5088Sab196087 dl_path, mod_name, cmd_name, optarg->oa_name); 1659*5088Sab196087 } 1660*5088Sab196087 1661*5088Sab196087 /* 1662*5088Sab196087 * Look up the specified module, loading the module if necessary, 1663*5088Sab196087 * and return its definition, or NULL on failure. 1664*5088Sab196087 * 1665*5088Sab196087 * entry: 1666*5088Sab196087 * name - Name of module to load. If name contains a '/' character or has 1667*5088Sab196087 * a ".so" suffix, then it is taken to be an absolute file path, 1668*5088Sab196087 * and is used directly as is. If name does not contain a '/' 1669*5088Sab196087 * character, then we look for it against the locations in 1670*5088Sab196087 * the module path, addint the '.so' suffix, and taking the first 1671*5088Sab196087 * one we find. 1672*5088Sab196087 * must_exist - If True, we consider it to be an error if we are unable 1673*5088Sab196087 * to locate a file to load and the module does not already exist. 1674*5088Sab196087 * If False, NULL is returned quietly in this case. 1675*5088Sab196087 * allow_abs - True if absolute paths are allowed. False to disallow 1676*5088Sab196087 * them. 1677*5088Sab196087 * 1678*5088Sab196087 * note: 1679*5088Sab196087 * If the path is absolute, then we load the file and take the module 1680*5088Sab196087 * name from the data returned by its elfedit_init() function. If a 1681*5088Sab196087 * module of that name is already loaded, it is unloaded and replaced 1682*5088Sab196087 * with the new one. 1683*5088Sab196087 * 1684*5088Sab196087 * If the path is non absolute, then we check to see if the module has 1685*5088Sab196087 * already been loaded, and if so, we return that module definition. 1686*5088Sab196087 * In this case, nothing new is loaded. If the module has not been loaded, 1687*5088Sab196087 * we search the path for it and load it. If the module name provided 1688*5088Sab196087 * by the elfedit_init() function does not match the name of the file, 1689*5088Sab196087 * an error results. 1690*5088Sab196087 */ 1691*5088Sab196087 elfeditGC_module_t * 1692*5088Sab196087 elfedit_load_module(const char *name, int must_exist, int allow_abs) 1693*5088Sab196087 { 1694*5088Sab196087 elfedit_init_func_t *init_func; 1695*5088Sab196087 elfeditGC_module_t *mod; 1696*5088Sab196087 MODLIST_T *moddef, *insdef; 1697*5088Sab196087 const char *path; 1698*5088Sab196087 char path_buf[PATH_MAX + 1]; 1699*5088Sab196087 void *hdl; 1700*5088Sab196087 size_t i; 1701*5088Sab196087 int is_abs_path; 1702*5088Sab196087 elfeditGC_cmd_t *cmd; 1703*5088Sab196087 1704*5088Sab196087 /* 1705*5088Sab196087 * If the name includes a .so suffix, or has any '/' characters, 1706*5088Sab196087 * then it is an absolute path that we use as is to load the named 1707*5088Sab196087 * file. Otherwise, we iterate over the path, adding the .so suffix 1708*5088Sab196087 * and load the first file that matches. 1709*5088Sab196087 */ 1710*5088Sab196087 is_abs_path = (path_is_so(name) != NULL) || 1711*5088Sab196087 (name != elfedit_basename(name, NULL, NULL, 0)); 1712*5088Sab196087 1713*5088Sab196087 if (is_abs_path && !allow_abs) 1714*5088Sab196087 load_module_err(NULL, NULL, NULL, 1715*5088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL); 1716*5088Sab196087 1717*5088Sab196087 /* 1718*5088Sab196087 * If this is a non-absolute path, search for the module already 1719*5088Sab196087 * having been loaded, and return it if so. 1720*5088Sab196087 */ 1721*5088Sab196087 if (!is_abs_path) { 1722*5088Sab196087 moddef = module_loaded(name, &insdef); 1723*5088Sab196087 if (moddef != NULL) 1724*5088Sab196087 return (moddef->ml_mod); 1725*5088Sab196087 /* 1726*5088Sab196087 * As a result of module_loaded(), insdef now contains the 1727*5088Sab196087 * immediate predecessor node for the new one, or NULL if 1728*5088Sab196087 * it goes at the front. In the absolute-path case, we take 1729*5088Sab196087 * care of this below, after the sharable object is loaded. 1730*5088Sab196087 */ 1731*5088Sab196087 } 1732*5088Sab196087 1733*5088Sab196087 /* 1734*5088Sab196087 * malloc() a module definition block before trying to dlopen(). 1735*5088Sab196087 * Doing things in the other order can cause the dlopen()'d object 1736*5088Sab196087 * to leak: If elfedit_malloc() fails, it can cause a jump to the 1737*5088Sab196087 * outer command loop without returning to the caller. Hence, 1738*5088Sab196087 * there will be no opportunity to clean up. Allocaing the module 1739*5088Sab196087 * first allows us to free it if necessary. 1740*5088Sab196087 */ 1741*5088Sab196087 moddef = elfedit_malloc(MSG_INTL(MSG_ALLOC_MODDEF), 1742*5088Sab196087 sizeof (*moddef) + PATH_MAX + 1); 1743*5088Sab196087 moddef->ml_path = ((char *)moddef) + sizeof (*moddef); 1744*5088Sab196087 1745*5088Sab196087 if (is_abs_path) { 1746*5088Sab196087 path = name; 1747*5088Sab196087 hdl = load_module_dlopen(name, moddef, must_exist); 1748*5088Sab196087 } else { 1749*5088Sab196087 hdl = NULL; 1750*5088Sab196087 path = path_buf; 1751*5088Sab196087 for (i = 0; i < state.modpath.n; i++) { 1752*5088Sab196087 if (snprintf(path_buf, sizeof (path_buf), 1753*5088Sab196087 MSG_ORIG(MSG_FMT_BLDSOPATH), state.modpath.seg[i], 1754*5088Sab196087 name) > sizeof (path_buf)) 1755*5088Sab196087 load_module_err(moddef, NULL, NULL, 1756*5088Sab196087 MSG_INTL(MSG_ERR_PATHTOOLONG), 1757*5088Sab196087 state.modpath.seg[i], name, NULL, NULL); 1758*5088Sab196087 hdl = load_module_dlopen(path, moddef, 0); 1759*5088Sab196087 } 1760*5088Sab196087 if (must_exist && (hdl == NULL)) 1761*5088Sab196087 load_module_err(moddef, NULL, NULL, 1762*5088Sab196087 MSG_INTL(MSG_ERR_UNRECMOD), name, NULL, NULL, NULL); 1763*5088Sab196087 } 1764*5088Sab196087 1765*5088Sab196087 if (hdl == NULL) { 1766*5088Sab196087 free(moddef); 1767*5088Sab196087 return (NULL); 1768*5088Sab196087 } 1769*5088Sab196087 1770*5088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 1771*5088Sab196087 init_func = (elfedit_init_func_t *) 1772*5088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT32)); 1773*5088Sab196087 } else { 1774*5088Sab196087 init_func = (elfedit_init_func_t *) 1775*5088Sab196087 dlsym(hdl, MSG_ORIG(MSG_STR_ELFEDITINIT64)); 1776*5088Sab196087 } 1777*5088Sab196087 if (init_func == NULL) 1778*5088Sab196087 load_module_err(moddef, hdl, path, 1779*5088Sab196087 MSG_INTL(MSG_ERR_SONOTMOD), path, NULL, NULL, NULL); 1780*5088Sab196087 1781*5088Sab196087 /* 1782*5088Sab196087 * Note that the init function will be passing us an 1783*5088Sab196087 * elfedit[32|64]_module_t pointer, which we cast to the 1784*5088Sab196087 * generic module pointer type in order to be able to manage 1785*5088Sab196087 * either type with one set of code. 1786*5088Sab196087 */ 1787*5088Sab196087 if (!(mod = (elfeditGC_module_t *)(* init_func)(ELFEDIT_VER_CURRENT))) 1788*5088Sab196087 load_module_err(moddef, hdl, path, 1789*5088Sab196087 MSG_INTL(MSG_ERR_BADMODLOAD), path, NULL, NULL, NULL); 1790*5088Sab196087 1791*5088Sab196087 /* 1792*5088Sab196087 * Enforce some rules, to help module developers: 1793*5088Sab196087 * - The primary name of a command must not be 1794*5088Sab196087 * the empty string (""). 1795*5088Sab196087 * - Options must start with a '-' followed by at least 1796*5088Sab196087 * one character. 1797*5088Sab196087 * - Arguments and options must be well formed. 1798*5088Sab196087 */ 1799*5088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) { 1800*5088Sab196087 if (**cmd->cmd_name == '\0') 1801*5088Sab196087 load_module_err(moddef, hdl, path, 1802*5088Sab196087 MSG_INTL(MSG_ERR_NULLPRICMDNAM), mod->mod_name, 1803*5088Sab196087 NULL, NULL, NULL); 1804*5088Sab196087 1805*5088Sab196087 if (cmd->cmd_args != NULL) 1806*5088Sab196087 validate_optarg(cmd->cmd_args, 0, moddef, mod->mod_name, 1807*5088Sab196087 cmd->cmd_name[0], hdl, path); 1808*5088Sab196087 if (cmd->cmd_opt != NULL) 1809*5088Sab196087 validate_optarg(cmd->cmd_opt, 1, moddef, mod->mod_name, 1810*5088Sab196087 cmd->cmd_name[0], hdl, path); 1811*5088Sab196087 } 1812*5088Sab196087 1813*5088Sab196087 /* 1814*5088Sab196087 * Check the name the module provides. How we handle this depends 1815*5088Sab196087 * on whether the path is absolute or the result of a path search. 1816*5088Sab196087 */ 1817*5088Sab196087 if (is_abs_path) { 1818*5088Sab196087 MODLIST_T *old_moddef = module_loaded(mod->mod_name, &insdef); 1819*5088Sab196087 1820*5088Sab196087 if (old_moddef != NULL) { /* Replace existing */ 1821*5088Sab196087 free(moddef); /* Rare case: Don't need it */ 1822*5088Sab196087 /* 1823*5088Sab196087 * Be sure we don't unload builtin modules! 1824*5088Sab196087 * These have a NULL dl_hdl field. 1825*5088Sab196087 */ 1826*5088Sab196087 if (old_moddef->ml_dl_hdl == NULL) 1827*5088Sab196087 load_module_err(NULL, hdl, path, 1828*5088Sab196087 MSG_INTL(MSG_ERR_CNTULSMOD), 1829*5088Sab196087 old_moddef->ml_mod->mod_name, NULL, 1830*5088Sab196087 NULL, NULL); 1831*5088Sab196087 1832*5088Sab196087 /* Unload existing */ 1833*5088Sab196087 if (dlclose(old_moddef->ml_dl_hdl) != 0) 1834*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 1835*5088Sab196087 MSG_INTL(MSG_ERR_CNTDLCLOSE), 1836*5088Sab196087 old_moddef->ml_path, dlerror()); 1837*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 1838*5088Sab196087 MSG_INTL(MSG_DEBUG_MODUNLOAD), 1839*5088Sab196087 old_moddef->ml_mod->mod_name, old_moddef->ml_path); 1840*5088Sab196087 old_moddef->ml_mod = mod; 1841*5088Sab196087 old_moddef->ml_dl_hdl = hdl; 1842*5088Sab196087 (void) strlcpy((char *)old_moddef->ml_path, path, 1843*5088Sab196087 PATH_MAX + 1); 1844*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 1845*5088Sab196087 MSG_INTL(MSG_DEBUG_MODLOAD), 1846*5088Sab196087 old_moddef->ml_mod->mod_name, path); 1847*5088Sab196087 return (old_moddef->ml_mod); 1848*5088Sab196087 } 1849*5088Sab196087 /* 1850*5088Sab196087 * insdef now contains the insertion point for the absolute 1851*5088Sab196087 * path case. 1852*5088Sab196087 */ 1853*5088Sab196087 } else { 1854*5088Sab196087 /* If the names don't match, then error */ 1855*5088Sab196087 if (strcasecmp(name, mod->mod_name) != 0) 1856*5088Sab196087 load_module_err(moddef, hdl, path, 1857*5088Sab196087 MSG_INTL(MSG_ERR_BADMODNAME), 1858*5088Sab196087 mod->mod_name, name, path, NULL); 1859*5088Sab196087 } 1860*5088Sab196087 1861*5088Sab196087 /* 1862*5088Sab196087 * Link module into the module list. If insdef is NULL, 1863*5088Sab196087 * it goes at the head. If insdef is non-NULL, it goes immediately 1864*5088Sab196087 * after 1865*5088Sab196087 */ 1866*5088Sab196087 if (insdef == NULL) { 1867*5088Sab196087 moddef->ml_next = state.modlist; 1868*5088Sab196087 state.modlist = moddef; 1869*5088Sab196087 } else { 1870*5088Sab196087 moddef->ml_next = insdef->ml_next; 1871*5088Sab196087 insdef->ml_next = moddef; 1872*5088Sab196087 } 1873*5088Sab196087 moddef->ml_mod = mod; 1874*5088Sab196087 moddef->ml_dl_hdl = hdl; 1875*5088Sab196087 (void) strlcpy((char *)moddef->ml_path, path, PATH_MAX + 1); 1876*5088Sab196087 1877*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODLOAD), 1878*5088Sab196087 moddef->ml_mod->mod_name, path); 1879*5088Sab196087 1880*5088Sab196087 return (moddef->ml_mod); 1881*5088Sab196087 } 1882*5088Sab196087 1883*5088Sab196087 1884*5088Sab196087 /* 1885*5088Sab196087 * Unload the specified module 1886*5088Sab196087 */ 1887*5088Sab196087 void 1888*5088Sab196087 elfedit_unload_module(const char *name) 1889*5088Sab196087 { 1890*5088Sab196087 MODLIST_T *moddef, *insdef; 1891*5088Sab196087 1892*5088Sab196087 moddef = module_loaded(name, &insdef); 1893*5088Sab196087 if (moddef == NULL) 1894*5088Sab196087 return; 1895*5088Sab196087 1896*5088Sab196087 /* Built in modules cannot be unloaded. They have a NULL dl_hdl field */ 1897*5088Sab196087 if (moddef->ml_dl_hdl == NULL) 1898*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTULSMOD), 1899*5088Sab196087 moddef->ml_mod->mod_name); 1900*5088Sab196087 1901*5088Sab196087 /* 1902*5088Sab196087 * When we unload it, the name string goes with it. So 1903*5088Sab196087 * announce it while we still can without having to make a copy. 1904*5088Sab196087 */ 1905*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_MODUNLOAD), 1906*5088Sab196087 moddef->ml_mod->mod_name, moddef->ml_path); 1907*5088Sab196087 1908*5088Sab196087 /* 1909*5088Sab196087 * Close it before going further. On failure, we'll jump, and the 1910*5088Sab196087 * record will remain in the module list. On success, 1911*5088Sab196087 * we'll retain control, and can safely remove it. 1912*5088Sab196087 */ 1913*5088Sab196087 if (dlclose(moddef->ml_dl_hdl) != 0) 1914*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTDLCLOSE), 1915*5088Sab196087 moddef->ml_path, dlerror()); 1916*5088Sab196087 1917*5088Sab196087 /* Unlink the record from the module list */ 1918*5088Sab196087 if (insdef == NULL) 1919*5088Sab196087 state.modlist = moddef->ml_next; 1920*5088Sab196087 else 1921*5088Sab196087 insdef->ml_next = moddef->ml_next; 1922*5088Sab196087 1923*5088Sab196087 /* Release the memory */ 1924*5088Sab196087 free(moddef); 1925*5088Sab196087 } 1926*5088Sab196087 1927*5088Sab196087 1928*5088Sab196087 /* 1929*5088Sab196087 * Load all sharable objects found in the specified directory. 1930*5088Sab196087 * 1931*5088Sab196087 * entry: 1932*5088Sab196087 * dirpath - Path of directory to process. 1933*5088Sab196087 * must_exist - If True, it is an error if diropen() fails to open 1934*5088Sab196087 * the given directory. Of False, we quietly ignore it and return. 1935*5088Sab196087 * abs_path - If True, files are loaded using their literal paths. 1936*5088Sab196087 * If False, their module name is extracted from the dirpath 1937*5088Sab196087 * and a path based search is used to locate it. 1938*5088Sab196087 */ 1939*5088Sab196087 void 1940*5088Sab196087 elfedit_load_moddir(const char *dirpath, int must_exist, int abs_path) 1941*5088Sab196087 { 1942*5088Sab196087 char path[PATH_MAX + 1]; 1943*5088Sab196087 DIR *dir; 1944*5088Sab196087 struct dirent *dp; 1945*5088Sab196087 const char *tail; 1946*5088Sab196087 1947*5088Sab196087 dir = opendir(dirpath); 1948*5088Sab196087 if (dir == NULL) { 1949*5088Sab196087 int err = errno; 1950*5088Sab196087 1951*5088Sab196087 if (!must_exist && (err == ENOENT)) 1952*5088Sab196087 return; 1953*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTOPNDIR), 1954*5088Sab196087 dirpath, strerror(err)); 1955*5088Sab196087 /*NOTREACHED*/ 1956*5088Sab196087 } 1957*5088Sab196087 1958*5088Sab196087 while (dp = readdir(dir)) { 1959*5088Sab196087 if ((tail = path_is_so(dp->d_name)) != NULL) { 1960*5088Sab196087 if (abs_path) { 1961*5088Sab196087 (void) snprintf(path, sizeof (path), 1962*5088Sab196087 MSG_ORIG(MSG_FMT_BLDPATH), dirpath, 1963*5088Sab196087 dp->d_name); 1964*5088Sab196087 } else { 1965*5088Sab196087 (void) elfedit_basename(dp->d_name, tail, 1966*5088Sab196087 path, sizeof (path)); 1967*5088Sab196087 } 1968*5088Sab196087 (void) elfedit_load_module(path, must_exist, 1); 1969*5088Sab196087 } 1970*5088Sab196087 } 1971*5088Sab196087 (void) closedir(dir); 1972*5088Sab196087 } 1973*5088Sab196087 1974*5088Sab196087 1975*5088Sab196087 /* 1976*5088Sab196087 * Follow the module load path, and load the first module found for each 1977*5088Sab196087 * given name. 1978*5088Sab196087 */ 1979*5088Sab196087 void 1980*5088Sab196087 elfedit_load_modpath(void) 1981*5088Sab196087 { 1982*5088Sab196087 size_t i; 1983*5088Sab196087 1984*5088Sab196087 for (i = 0; i < state.modpath.n; i++) 1985*5088Sab196087 elfedit_load_moddir(state.modpath.seg[i], 0, 0); 1986*5088Sab196087 } 1987*5088Sab196087 1988*5088Sab196087 /* 1989*5088Sab196087 * Given a module definition, look for the specified command. 1990*5088Sab196087 * Returns the command if found, and NULL otherwise. 1991*5088Sab196087 */ 1992*5088Sab196087 static elfeditGC_cmd_t * 1993*5088Sab196087 find_cmd(elfeditGC_module_t *mod, const char *name) 1994*5088Sab196087 { 1995*5088Sab196087 elfeditGC_cmd_t *cmd; 1996*5088Sab196087 const char **cmd_name; 1997*5088Sab196087 1998*5088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) 1999*5088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++) 2000*5088Sab196087 if (strcasecmp(name, *cmd_name) == 0) { 2001*5088Sab196087 if (cmd_name != cmd->cmd_name) 2002*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 2003*5088Sab196087 MSG_INTL(MSG_DEBUG_CMDALIAS), 2004*5088Sab196087 mod->mod_name, *cmd_name, 2005*5088Sab196087 mod->mod_name, *cmd->cmd_name); 2006*5088Sab196087 return (cmd); 2007*5088Sab196087 } 2008*5088Sab196087 2009*5088Sab196087 return (NULL); 2010*5088Sab196087 } 2011*5088Sab196087 2012*5088Sab196087 2013*5088Sab196087 /* 2014*5088Sab196087 * Given a command name, return its command definition. 2015*5088Sab196087 * 2016*5088Sab196087 * entry: 2017*5088Sab196087 * name - Command to be looked up 2018*5088Sab196087 * must_exist - If True, we consider it to be an error if the command 2019*5088Sab196087 * does not exist. If False, NULL is returned quietly in 2020*5088Sab196087 * this case. 2021*5088Sab196087 * mod_ret - NULL, or address of a variable to receive the 2022*5088Sab196087 * module definition block of the module containing 2023*5088Sab196087 * the command. 2024*5088Sab196087 * 2025*5088Sab196087 * exit: 2026*5088Sab196087 * On success, returns a pointer to the command definition, and 2027*5088Sab196087 * if mod_ret is non-NULL, *mod_ret receives a pointer to the 2028*5088Sab196087 * module definition. On failure, must_exist determines the 2029*5088Sab196087 * action taken: If must_exist is True, an error is issued and 2030*5088Sab196087 * control does not return to the caller. If must_exist is False, 2031*5088Sab196087 * NULL is quietly returned. 2032*5088Sab196087 * 2033*5088Sab196087 * note: 2034*5088Sab196087 * A ':' in name is used to delimit the module and command names. 2035*5088Sab196087 * If it is omitted, or if it is the first non-whitespace character 2036*5088Sab196087 * in the name, then the built in sys: module is implied. 2037*5088Sab196087 */ 2038*5088Sab196087 elfeditGC_cmd_t * 2039*5088Sab196087 elfedit_find_command(const char *name, int must_exist, 2040*5088Sab196087 elfeditGC_module_t **mod_ret) 2041*5088Sab196087 { 2042*5088Sab196087 elfeditGC_module_t *mod; 2043*5088Sab196087 const char *mod_str; 2044*5088Sab196087 const char *cmd_str; 2045*5088Sab196087 char mod_buf[ELFEDIT_MAXMODNAM + 1]; 2046*5088Sab196087 size_t n; 2047*5088Sab196087 elfeditGC_cmd_t *cmd; 2048*5088Sab196087 2049*5088Sab196087 2050*5088Sab196087 cmd_str = strstr(name, MSG_ORIG(MSG_STR_COLON)); 2051*5088Sab196087 if (cmd_str == NULL) { /* No module name -> sys: */ 2052*5088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS); 2053*5088Sab196087 cmd_str = name; 2054*5088Sab196087 } else if (cmd_str == name) { /* Empty module name -> sys: */ 2055*5088Sab196087 mod_str = MSG_ORIG(MSG_MOD_SYS); 2056*5088Sab196087 cmd_str++; /* Skip the colon */ 2057*5088Sab196087 } else { /* Have both module and command */ 2058*5088Sab196087 n = cmd_str - name; 2059*5088Sab196087 if (n >= sizeof (mod_buf)) { 2060*5088Sab196087 if (must_exist) 2061*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 2062*5088Sab196087 MSG_INTL(MSG_ERR_MODNAMTOOLONG), name); 2063*5088Sab196087 return (NULL); 2064*5088Sab196087 } 2065*5088Sab196087 (void) strlcpy(mod_buf, name, n + 1); 2066*5088Sab196087 mod_str = mod_buf; 2067*5088Sab196087 cmd_str++; 2068*5088Sab196087 } 2069*5088Sab196087 2070*5088Sab196087 /* Lookup/load module. Won't return on error */ 2071*5088Sab196087 mod = elfedit_load_module(mod_str, must_exist, 0); 2072*5088Sab196087 if (mod == NULL) 2073*5088Sab196087 return (NULL); 2074*5088Sab196087 2075*5088Sab196087 /* Locate the command */ 2076*5088Sab196087 cmd = find_cmd(mod, cmd_str); 2077*5088Sab196087 if (cmd == NULL) { 2078*5088Sab196087 if (must_exist) { 2079*5088Sab196087 /* 2080*5088Sab196087 * Catch empty command in order to provide 2081*5088Sab196087 * a better error message. 2082*5088Sab196087 */ 2083*5088Sab196087 if (*cmd_str == '\0') { 2084*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 2085*5088Sab196087 MSG_INTL(MSG_ERR_MODNOCMD), mod_str); 2086*5088Sab196087 } else { 2087*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 2088*5088Sab196087 MSG_INTL(MSG_ERR_UNRECCMD), 2089*5088Sab196087 mod_str, cmd_str); 2090*5088Sab196087 } 2091*5088Sab196087 } 2092*5088Sab196087 } else { 2093*5088Sab196087 if (mod_ret != NULL) 2094*5088Sab196087 *mod_ret = mod; 2095*5088Sab196087 } 2096*5088Sab196087 return (cmd); 2097*5088Sab196087 } 2098*5088Sab196087 2099*5088Sab196087 2100*5088Sab196087 /* 2101*5088Sab196087 * Release all user command blocks found on state.ucmd 2102*5088Sab196087 */ 2103*5088Sab196087 static void 2104*5088Sab196087 free_user_cmds(void) 2105*5088Sab196087 { 2106*5088Sab196087 USER_CMD_T *next; 2107*5088Sab196087 2108*5088Sab196087 while (state.ucmd.list) { 2109*5088Sab196087 next = state.ucmd.list->ucmd_next; 2110*5088Sab196087 free(state.ucmd.list); 2111*5088Sab196087 state.ucmd.list = next; 2112*5088Sab196087 } 2113*5088Sab196087 state.ucmd.tail = NULL; 2114*5088Sab196087 state.ucmd.n = 0; 2115*5088Sab196087 state.cur_cmd = NULL; 2116*5088Sab196087 } 2117*5088Sab196087 2118*5088Sab196087 2119*5088Sab196087 /* 2120*5088Sab196087 * Process all user command blocks found on state.ucmd, and then 2121*5088Sab196087 * remove them from the list. 2122*5088Sab196087 */ 2123*5088Sab196087 static void 2124*5088Sab196087 dispatch_user_cmds() 2125*5088Sab196087 { 2126*5088Sab196087 USER_CMD_T *ucmd; 2127*5088Sab196087 elfedit_cmdret_t cmd_ret; 2128*5088Sab196087 2129*5088Sab196087 ucmd = state.ucmd.list; 2130*5088Sab196087 if (ucmd) { 2131*5088Sab196087 /* Do them, in order */ 2132*5088Sab196087 for (; ucmd; ucmd = ucmd->ucmd_next) { 2133*5088Sab196087 state.cur_cmd = ucmd; 2134*5088Sab196087 if (!state.msg_jbuf.active) 2135*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 2136*5088Sab196087 MSG_INTL(MSG_DEBUG_EXECCMD), 2137*5088Sab196087 ucmd->ucmd_orig_str); 2138*5088Sab196087 /* 2139*5088Sab196087 * The cmd_func field is the generic definition. 2140*5088Sab196087 * We need to cast it to the type that matches 2141*5088Sab196087 * the proper ELFCLASS before calling it. 2142*5088Sab196087 */ 2143*5088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 2144*5088Sab196087 elfedit32_cmd_func_t *cmd_func = 2145*5088Sab196087 (elfedit32_cmd_func_t *) 2146*5088Sab196087 ucmd->ucmd_cmd->cmd_func; 2147*5088Sab196087 2148*5088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s32, 2149*5088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv); 2150*5088Sab196087 } else { 2151*5088Sab196087 elfedit64_cmd_func_t *cmd_func = 2152*5088Sab196087 (elfedit64_cmd_func_t *) 2153*5088Sab196087 ucmd->ucmd_cmd->cmd_func; 2154*5088Sab196087 2155*5088Sab196087 cmd_ret = (* cmd_func)(state.elf.obj_state.s64, 2156*5088Sab196087 ucmd->ucmd_argc, ucmd->ucmd_argv); 2157*5088Sab196087 } 2158*5088Sab196087 state.cur_cmd = NULL; 2159*5088Sab196087 /* If a pager was started, wrap it up */ 2160*5088Sab196087 elfedit_pager_cleanup(); 2161*5088Sab196087 2162*5088Sab196087 switch (cmd_ret) { 2163*5088Sab196087 case ELFEDIT_CMDRET_MOD: 2164*5088Sab196087 /* 2165*5088Sab196087 * Command modified the output ELF image, 2166*5088Sab196087 * mark the file as needing a flush to disk. 2167*5088Sab196087 */ 2168*5088Sab196087 state.file.dirty = 1; 2169*5088Sab196087 break; 2170*5088Sab196087 case ELFEDIT_CMDRET_FLUSH: 2171*5088Sab196087 /* 2172*5088Sab196087 * Command flushed the output file, 2173*5088Sab196087 * clear the dirty bit. 2174*5088Sab196087 */ 2175*5088Sab196087 state.file.dirty = 0; 2176*5088Sab196087 } 2177*5088Sab196087 } 2178*5088Sab196087 free_user_cmds(); 2179*5088Sab196087 } 2180*5088Sab196087 } 2181*5088Sab196087 2182*5088Sab196087 2183*5088Sab196087 /* 2184*5088Sab196087 * Prepare a GETTOK_STATE struct for gettok(). 2185*5088Sab196087 * 2186*5088Sab196087 * entry: 2187*5088Sab196087 * gettok_state - gettok state block to use 2188*5088Sab196087 * str - Writable buffer to tokenize. Note that gettok() 2189*5088Sab196087 * is allowed to change the contents of this buffer. 2190*5088Sab196087 * inc_null_final - If the line ends in whitespace instead of 2191*5088Sab196087 * immediately hitting a NULL, and inc_null_final is TRUE, 2192*5088Sab196087 * then a null final token is generated. Otherwise trailing 2193*5088Sab196087 * whitespace is ignored. 2194*5088Sab196087 */ 2195*5088Sab196087 static void 2196*5088Sab196087 gettok_init(GETTOK_STATE *gettok_state, char *buf, int inc_null_final) 2197*5088Sab196087 { 2198*5088Sab196087 gettok_state->gtok_buf = gettok_state->gtok_cur_buf = buf; 2199*5088Sab196087 gettok_state->gtok_inc_null_final = inc_null_final; 2200*5088Sab196087 gettok_state->gtok_null_seen = 0; 2201*5088Sab196087 } 2202*5088Sab196087 2203*5088Sab196087 2204*5088Sab196087 /* 2205*5088Sab196087 * Locate the next token from the buffer. 2206*5088Sab196087 * 2207*5088Sab196087 * entry: 2208*5088Sab196087 * gettok_state - State of gettok() operation. Initialized 2209*5088Sab196087 * by gettok_init(), and passed to gettok(). 2210*5088Sab196087 * 2211*5088Sab196087 * exit: 2212*5088Sab196087 * If a token is found, gettok_state->gtok_last_token is filled in 2213*5088Sab196087 * with the details and True (1) is returned. If no token is found, 2214*5088Sab196087 * False (1) is returned, and the contents of 2215*5088Sab196087 * gettok_state->gtok_last_token are undefined. 2216*5088Sab196087 * 2217*5088Sab196087 * note: 2218*5088Sab196087 * - The token returned references the memory in gettok_state->gtok_buf. 2219*5088Sab196087 * The caller should not modify the buffer until all such 2220*5088Sab196087 * pointers have been discarded. 2221*5088Sab196087 * - This routine will modify the contents of gettok_state->gtok_buf 2222*5088Sab196087 * as necessary to remove quotes and eliminate escape 2223*5088Sab196087 * (\)characters. 2224*5088Sab196087 */ 2225*5088Sab196087 static int 2226*5088Sab196087 gettok(GETTOK_STATE *gettok_state) 2227*5088Sab196087 { 2228*5088Sab196087 char *str = gettok_state->gtok_cur_buf; 2229*5088Sab196087 char *look; 2230*5088Sab196087 int quote_ch = '\0'; 2231*5088Sab196087 2232*5088Sab196087 /* Skip leading whitespace */ 2233*5088Sab196087 while (isspace(*str)) 2234*5088Sab196087 str++; 2235*5088Sab196087 2236*5088Sab196087 if (*str == '\0') { 2237*5088Sab196087 /* 2238*5088Sab196087 * If user requested it, and there was whitespace at the 2239*5088Sab196087 * end, then generate one last null token. 2240*5088Sab196087 */ 2241*5088Sab196087 if (gettok_state->gtok_inc_null_final && 2242*5088Sab196087 !gettok_state->gtok_null_seen) { 2243*5088Sab196087 gettok_state->gtok_inc_null_final = 0; 2244*5088Sab196087 gettok_state->gtok_null_seen = 1; 2245*5088Sab196087 gettok_state->gtok_last_token.tok_str = str; 2246*5088Sab196087 gettok_state->gtok_last_token.tok_len = 0; 2247*5088Sab196087 gettok_state->gtok_last_token.tok_line_off = 2248*5088Sab196087 str - gettok_state->gtok_buf; 2249*5088Sab196087 return (1); 2250*5088Sab196087 } 2251*5088Sab196087 gettok_state->gtok_null_seen = 1; 2252*5088Sab196087 return (0); 2253*5088Sab196087 } 2254*5088Sab196087 2255*5088Sab196087 /* 2256*5088Sab196087 * Read token: The standard delimiter is whitespace, but 2257*5088Sab196087 * we honor either single or double quotes. Also, we honor 2258*5088Sab196087 * backslash escapes. 2259*5088Sab196087 */ 2260*5088Sab196087 gettok_state->gtok_last_token.tok_str = look = str; 2261*5088Sab196087 gettok_state->gtok_last_token.tok_line_off = 2262*5088Sab196087 look - gettok_state->gtok_buf; 2263*5088Sab196087 for (; *look; look++) { 2264*5088Sab196087 if (*look == quote_ch) { /* Terminates active quote */ 2265*5088Sab196087 quote_ch = '\0'; 2266*5088Sab196087 continue; 2267*5088Sab196087 } 2268*5088Sab196087 2269*5088Sab196087 if (quote_ch == '\0') { /* No quote currently active */ 2270*5088Sab196087 if ((*look == '\'') || (*look == '"')) { 2271*5088Sab196087 quote_ch = *look; /* New active quote */ 2272*5088Sab196087 continue; 2273*5088Sab196087 } 2274*5088Sab196087 if (isspace(*look)) 2275*5088Sab196087 break; 2276*5088Sab196087 } 2277*5088Sab196087 2278*5088Sab196087 if (*look == '\\') { 2279*5088Sab196087 look++; 2280*5088Sab196087 if (*look == '\0') /* Esc applied to NULL term? */ 2281*5088Sab196087 break; 2282*5088Sab196087 } 2283*5088Sab196087 2284*5088Sab196087 if (look != str) 2285*5088Sab196087 *str = *look; 2286*5088Sab196087 str++; 2287*5088Sab196087 } 2288*5088Sab196087 gettok_state->gtok_last_token.tok_len = str - 2289*5088Sab196087 gettok_state->gtok_last_token.tok_str; 2290*5088Sab196087 gettok_state->gtok_null_seen = *look == '\0'; 2291*5088Sab196087 if (!gettok_state->gtok_null_seen) 2292*5088Sab196087 look++; 2293*5088Sab196087 *str = '\0'; 2294*5088Sab196087 gettok_state->gtok_cur_buf = look; 2295*5088Sab196087 2296*5088Sab196087 #if 0 2297*5088Sab196087 printf("GETTOK >%s< len(%d) offset(%d)\n", 2298*5088Sab196087 gettok_state->gtok_last_token.tok_str, 2299*5088Sab196087 gettok_state->gtok_last_token.tok_len, 2300*5088Sab196087 gettok_state->gtok_last_token.tok_line_off); 2301*5088Sab196087 #endif 2302*5088Sab196087 2303*5088Sab196087 return (1); 2304*5088Sab196087 } 2305*5088Sab196087 2306*5088Sab196087 2307*5088Sab196087 /* 2308*5088Sab196087 * Tokenize the user command string, and return a pointer to the 2309*5088Sab196087 * TOK_STATE buffer maintained by this function. That buffer contains 2310*5088Sab196087 * the tokenized strings. 2311*5088Sab196087 * 2312*5088Sab196087 * entry: 2313*5088Sab196087 * user_cmd_str - String to tokenize 2314*5088Sab196087 * len - # of characters in user_cmd_str to examine. If 2315*5088Sab196087 * (len < 0), then the complete string is processed 2316*5088Sab196087 * stopping with the NULL termination. Otherwise, 2317*5088Sab196087 * processing stops after len characters, and any 2318*5088Sab196087 * remaining characters are ignored. 2319*5088Sab196087 * inc_null_final - If True, and if user_cmd_str has whitespace 2320*5088Sab196087 * at the end following the last non-null token, then 2321*5088Sab196087 * a final null token will be included. If False, null 2322*5088Sab196087 * tokens are ignored. 2323*5088Sab196087 * 2324*5088Sab196087 * note: 2325*5088Sab196087 * This routine returns pointers to internally allocated memory. 2326*5088Sab196087 * The caller must not alter anything contained in the TOK_STATE 2327*5088Sab196087 * buffer returned. Furthermore, the the contents of TOK_STATE 2328*5088Sab196087 * are only valid until the next call to tokenize_user_cmd(). 2329*5088Sab196087 */ 2330*5088Sab196087 static TOK_STATE * 2331*5088Sab196087 tokenize_user_cmd(const char *user_cmd_str, size_t len, int inc_null_final) 2332*5088Sab196087 { 2333*5088Sab196087 #define INITIAL_TOK_ALLOC 5 2334*5088Sab196087 2335*5088Sab196087 /* 2336*5088Sab196087 * As we parse the user command, we need temporary space to 2337*5088Sab196087 * hold the tokens. We do this by dynamically allocating a string 2338*5088Sab196087 * buffer and a token array, and doubling them as necessary. This 2339*5088Sab196087 * is a single threaded application, so static variables suffice. 2340*5088Sab196087 */ 2341*5088Sab196087 static STRBUF str; 2342*5088Sab196087 static TOK_STATE tokst; 2343*5088Sab196087 2344*5088Sab196087 GETTOK_STATE gettok_state; 2345*5088Sab196087 size_t n; 2346*5088Sab196087 2347*5088Sab196087 /* 2348*5088Sab196087 * Make a copy we can modify. If (len == 0), take the entire 2349*5088Sab196087 * string. Otherwise limit it to the specified length. 2350*5088Sab196087 */ 2351*5088Sab196087 tokst.tokst_cmd_len = strlen(user_cmd_str); 2352*5088Sab196087 if ((len > 0) && (len < tokst.tokst_cmd_len)) 2353*5088Sab196087 tokst.tokst_cmd_len = len; 2354*5088Sab196087 tokst.tokst_cmd_len++; /* Room for NULL termination */ 2355*5088Sab196087 strbuf_ensure_size(&str, tokst.tokst_cmd_len); 2356*5088Sab196087 (void) strlcpy(str.buf, user_cmd_str, tokst.tokst_cmd_len); 2357*5088Sab196087 2358*5088Sab196087 /* Trim off any newline character that might be present */ 2359*5088Sab196087 if ((tokst.tokst_cmd_len > 1) && 2360*5088Sab196087 (str.buf[tokst.tokst_cmd_len - 2] == '\n')) { 2361*5088Sab196087 tokst.tokst_cmd_len--; 2362*5088Sab196087 str.buf[tokst.tokst_cmd_len - 1] = '\0'; 2363*5088Sab196087 } 2364*5088Sab196087 2365*5088Sab196087 /* Tokenize the user command string into tok struct */ 2366*5088Sab196087 gettok_init(&gettok_state, str.buf, inc_null_final); 2367*5088Sab196087 tokst.tokst_str_size = 0; /* Space needed for token strings */ 2368*5088Sab196087 for (tokst.tokst_cnt = 0; gettok(&gettok_state) != 0; 2369*5088Sab196087 tokst.tokst_cnt++) { 2370*5088Sab196087 /* If we need more room, expand the token buffer */ 2371*5088Sab196087 if (tokst.tokst_cnt >= tokst.tokst_bufsize) { 2372*5088Sab196087 n = (tokst.tokst_bufsize == 0) ? 2373*5088Sab196087 INITIAL_TOK_ALLOC : (tokst.tokst_bufsize * 2); 2374*5088Sab196087 tokst.tokst_buf = elfedit_realloc( 2375*5088Sab196087 MSG_INTL(MSG_ALLOC_TOKBUF), tokst.tokst_buf, 2376*5088Sab196087 n * sizeof (*tokst.tokst_buf)); 2377*5088Sab196087 tokst.tokst_bufsize = n; 2378*5088Sab196087 } 2379*5088Sab196087 tokst.tokst_str_size += 2380*5088Sab196087 gettok_state.gtok_last_token.tok_len + 1; 2381*5088Sab196087 tokst.tokst_buf[tokst.tokst_cnt] = gettok_state.gtok_last_token; 2382*5088Sab196087 } 2383*5088Sab196087 /* fold the command token to lowercase */ 2384*5088Sab196087 if (tokst.tokst_cnt > 0) { 2385*5088Sab196087 char *s; 2386*5088Sab196087 2387*5088Sab196087 for (s = tokst.tokst_buf[0].tok_str; *s; s++) 2388*5088Sab196087 if (isupper(*s)) 2389*5088Sab196087 *s = tolower(*s); 2390*5088Sab196087 } 2391*5088Sab196087 2392*5088Sab196087 return (&tokst); 2393*5088Sab196087 2394*5088Sab196087 #undef INITIAL_TOK_ALLOC 2395*5088Sab196087 } 2396*5088Sab196087 2397*5088Sab196087 2398*5088Sab196087 /* 2399*5088Sab196087 * Parse the user command string, and put an entry for it at the end 2400*5088Sab196087 * of state.ucmd. 2401*5088Sab196087 */ 2402*5088Sab196087 static void 2403*5088Sab196087 parse_user_cmd(const char *user_cmd_str) 2404*5088Sab196087 { 2405*5088Sab196087 TOK_STATE *tokst; 2406*5088Sab196087 char *s; 2407*5088Sab196087 size_t n; 2408*5088Sab196087 size_t len; 2409*5088Sab196087 USER_CMD_T *ucmd; 2410*5088Sab196087 elfeditGC_module_t *mod; 2411*5088Sab196087 elfeditGC_cmd_t *cmd; 2412*5088Sab196087 2413*5088Sab196087 /* 2414*5088Sab196087 * Break it into tokens. If there are none, then it is 2415*5088Sab196087 * an empty command and is ignored. 2416*5088Sab196087 */ 2417*5088Sab196087 tokst = tokenize_user_cmd(user_cmd_str, -1, 0); 2418*5088Sab196087 if (tokst->tokst_cnt == 0) 2419*5088Sab196087 return; 2420*5088Sab196087 2421*5088Sab196087 /* Find the command. Won't return on error */ 2422*5088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 1, &mod); 2423*5088Sab196087 2424*5088Sab196087 /* 2425*5088Sab196087 * If there is no ELF file being edited, then only commands 2426*5088Sab196087 * from the sys: module are allowed. 2427*5088Sab196087 */ 2428*5088Sab196087 if ((state.file.present == 0) && 2429*5088Sab196087 (strcmp(mod->mod_name, MSG_ORIG(MSG_MOD_SYS)) != 0)) 2430*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOFILSYSONLY), 2431*5088Sab196087 mod->mod_name, cmd->cmd_name[0]); 2432*5088Sab196087 2433*5088Sab196087 2434*5088Sab196087 /* Allocate, fill in, and insert a USER_CMD_T block */ 2435*5088Sab196087 n = S_DROUND(sizeof (USER_CMD_T)); 2436*5088Sab196087 ucmd = elfedit_malloc(MSG_INTL(MSG_ALLOC_UCMD), 2437*5088Sab196087 n + (sizeof (char *) * (tokst->tokst_cnt - 1)) + 2438*5088Sab196087 tokst->tokst_cmd_len + tokst->tokst_str_size); 2439*5088Sab196087 ucmd->ucmd_next = NULL; 2440*5088Sab196087 ucmd->ucmd_argc = tokst->tokst_cnt - 1; 2441*5088Sab196087 /*LINTED E_BAD_PTR_CAST_ALIGN*/ 2442*5088Sab196087 ucmd->ucmd_argv = (const char **)(n + (char *)ucmd); 2443*5088Sab196087 ucmd->ucmd_orig_str = (char *)(ucmd->ucmd_argv + ucmd->ucmd_argc); 2444*5088Sab196087 (void) strncpy(ucmd->ucmd_orig_str, user_cmd_str, tokst->tokst_cmd_len); 2445*5088Sab196087 ucmd->ucmd_mod = mod; 2446*5088Sab196087 ucmd->ucmd_cmd = cmd; 2447*5088Sab196087 ucmd->ucmd_ostyle_set = 0; 2448*5088Sab196087 s = ucmd->ucmd_orig_str + tokst->tokst_cmd_len; 2449*5088Sab196087 for (n = 1; n < tokst->tokst_cnt; n++) { 2450*5088Sab196087 len = tokst->tokst_buf[n].tok_len + 1; 2451*5088Sab196087 ucmd->ucmd_argv[n - 1] = s; 2452*5088Sab196087 (void) strncpy(s, tokst->tokst_buf[n].tok_str, len); 2453*5088Sab196087 s += len; 2454*5088Sab196087 } 2455*5088Sab196087 if (state.ucmd.list == NULL) { 2456*5088Sab196087 state.ucmd.list = state.ucmd.tail = ucmd; 2457*5088Sab196087 } else { 2458*5088Sab196087 state.ucmd.tail->ucmd_next = ucmd; 2459*5088Sab196087 state.ucmd.tail = ucmd; 2460*5088Sab196087 } 2461*5088Sab196087 state.ucmd.n++; 2462*5088Sab196087 } 2463*5088Sab196087 2464*5088Sab196087 2465*5088Sab196087 /* 2466*5088Sab196087 * Copy infile to a new file with the name given by outfile. 2467*5088Sab196087 */ 2468*5088Sab196087 static void 2469*5088Sab196087 create_outfile(const char *infile, const char *outfile) 2470*5088Sab196087 { 2471*5088Sab196087 pid_t pid; 2472*5088Sab196087 int statloc; 2473*5088Sab196087 struct stat statbuf; 2474*5088Sab196087 2475*5088Sab196087 2476*5088Sab196087 pid = fork(); 2477*5088Sab196087 switch (pid) { 2478*5088Sab196087 case -1: /* Unable to create process */ 2479*5088Sab196087 { 2480*5088Sab196087 int err = errno; 2481*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTFORK), 2482*5088Sab196087 strerror(err)); 2483*5088Sab196087 } 2484*5088Sab196087 /*NOTREACHED*/ 2485*5088Sab196087 return; 2486*5088Sab196087 2487*5088Sab196087 case 0: 2488*5088Sab196087 (void) execl(MSG_ORIG(MSG_STR_BINCP), 2489*5088Sab196087 MSG_ORIG(MSG_STR_BINCP), infile, outfile, NULL); 2490*5088Sab196087 /* 2491*5088Sab196087 * exec() only returns on error. This is the child process, 2492*5088Sab196087 * so we want to stay away from the usual error mechanism 2493*5088Sab196087 * and handle things directly. 2494*5088Sab196087 */ 2495*5088Sab196087 { 2496*5088Sab196087 int err = errno; 2497*5088Sab196087 (void) fprintf(stderr, MSG_INTL(MSG_ERR_CNTEXEC), 2498*5088Sab196087 MSG_ORIG(MSG_STR_ELFEDIT), 2499*5088Sab196087 MSG_ORIG(MSG_STR_BINCP), strerror(err)); 2500*5088Sab196087 } 2501*5088Sab196087 exit(1); 2502*5088Sab196087 /*NOTREACHED*/ 2503*5088Sab196087 } 2504*5088Sab196087 2505*5088Sab196087 /* This is the parent: Wait for the child to terminate */ 2506*5088Sab196087 if (waitpid(pid, &statloc, 0) != pid) { 2507*5088Sab196087 int err = errno; 2508*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTWAIT), 2509*5088Sab196087 strerror(err)); 2510*5088Sab196087 } 2511*5088Sab196087 /* 2512*5088Sab196087 * If the child failed, then terminate the process. There is no 2513*5088Sab196087 * need for an error message, because the child will have taken 2514*5088Sab196087 * care of that. 2515*5088Sab196087 */ 2516*5088Sab196087 if (!WIFEXITED(statloc) || (WEXITSTATUS(statloc) != 0)) 2517*5088Sab196087 exit(1); 2518*5088Sab196087 2519*5088Sab196087 /* Make sure the copy allows user write access */ 2520*5088Sab196087 if (stat(outfile, &statbuf) == -1) { 2521*5088Sab196087 int err = errno; 2522*5088Sab196087 (void) unlink(outfile); 2523*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTSTAT), 2524*5088Sab196087 outfile, strerror(err)); 2525*5088Sab196087 } 2526*5088Sab196087 if ((statbuf.st_mode & S_IWUSR) == 0) { 2527*5088Sab196087 /* Only keep permission bits, and add user write */ 2528*5088Sab196087 statbuf.st_mode |= S_IWUSR; 2529*5088Sab196087 statbuf.st_mode &= 07777; /* Only keep the permission bits */ 2530*5088Sab196087 if (chmod(outfile, statbuf.st_mode) == -1) { 2531*5088Sab196087 int err = errno; 2532*5088Sab196087 (void) unlink(outfile); 2533*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTCHMOD), 2534*5088Sab196087 outfile, strerror(err)); 2535*5088Sab196087 } 2536*5088Sab196087 } 2537*5088Sab196087 } 2538*5088Sab196087 2539*5088Sab196087 /* 2540*5088Sab196087 * Given a module path string, determine how long the resulting path will 2541*5088Sab196087 * be when all % tokens have been expanded. 2542*5088Sab196087 * 2543*5088Sab196087 * entry: 2544*5088Sab196087 * path - Path for which expanded length is desired 2545*5088Sab196087 * origin_root - Root of $ORIGIN tree containing running elfedit program 2546*5088Sab196087 * 2547*5088Sab196087 * exit: 2548*5088Sab196087 * Returns the value strlen() will give for the expanded path. 2549*5088Sab196087 */ 2550*5088Sab196087 static size_t 2551*5088Sab196087 modpath_strlen(const char *path, const char *origin_root) 2552*5088Sab196087 { 2553*5088Sab196087 size_t len = 0; 2554*5088Sab196087 const char *s; 2555*5088Sab196087 2556*5088Sab196087 s = path; 2557*5088Sab196087 len = 0; 2558*5088Sab196087 for (s = path; *s != '\0'; s++) { 2559*5088Sab196087 if (*s == '%') { 2560*5088Sab196087 s++; 2561*5088Sab196087 switch (*s) { 2562*5088Sab196087 case 'i': /* ISA of running elfedit */ 2563*5088Sab196087 len += strlen(isa_i_str); 2564*5088Sab196087 break; 2565*5088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */ 2566*5088Sab196087 len += strlen(isa_I_str); 2567*5088Sab196087 break; 2568*5088Sab196087 case 'o': /* Insert default path */ 2569*5088Sab196087 len += 2570*5088Sab196087 modpath_strlen(MSG_ORIG(MSG_STR_MODPATH), 2571*5088Sab196087 origin_root); 2572*5088Sab196087 break; 2573*5088Sab196087 case 'r': /* root of tree with running elfedit */ 2574*5088Sab196087 len += strlen(origin_root); 2575*5088Sab196087 break; 2576*5088Sab196087 2577*5088Sab196087 case '%': /* %% is reduced to just '%' */ 2578*5088Sab196087 len++; 2579*5088Sab196087 break; 2580*5088Sab196087 default: /* All other % codes are reserved */ 2581*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 2582*5088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *s); 2583*5088Sab196087 /*NOTREACHED*/ 2584*5088Sab196087 break; 2585*5088Sab196087 } 2586*5088Sab196087 } else { /* Non-% character passes straight through */ 2587*5088Sab196087 len++; 2588*5088Sab196087 } 2589*5088Sab196087 } 2590*5088Sab196087 2591*5088Sab196087 return (len); 2592*5088Sab196087 } 2593*5088Sab196087 2594*5088Sab196087 2595*5088Sab196087 /* 2596*5088Sab196087 * Given a module path string, and a buffer large enough to hold the results, 2597*5088Sab196087 * fill the buffer with the expanded path. 2598*5088Sab196087 * 2599*5088Sab196087 * entry: 2600*5088Sab196087 * path - Path for which expanded length is desired 2601*5088Sab196087 * origin_root - Root of tree containing running elfedit program 2602*5088Sab196087 * buf - Buffer to receive the result. buf must as large or larger 2603*5088Sab196087 * than the value given by modpath_strlen(). 2604*5088Sab196087 * 2605*5088Sab196087 * exit: 2606*5088Sab196087 * Returns pointer to location following the last character 2607*5088Sab196087 * written to buf. A NULL byte is written to that address. 2608*5088Sab196087 */ 2609*5088Sab196087 static char * 2610*5088Sab196087 modpath_expand(const char *path, const char *origin_root, char *buf) 2611*5088Sab196087 { 2612*5088Sab196087 size_t len; 2613*5088Sab196087 const char *cp_str; 2614*5088Sab196087 2615*5088Sab196087 for (; *path != '\0'; path++) { 2616*5088Sab196087 if (*path == '%') { 2617*5088Sab196087 path++; 2618*5088Sab196087 cp_str = NULL; 2619*5088Sab196087 switch (*path) { 2620*5088Sab196087 case 'i': /* ISA of running elfedit */ 2621*5088Sab196087 cp_str = isa_i_str; 2622*5088Sab196087 break; 2623*5088Sab196087 case 'I': /* "" for 32-bit, same as %i for 64 */ 2624*5088Sab196087 cp_str = isa_I_str; 2625*5088Sab196087 break; 2626*5088Sab196087 case 'o': /* Insert default path */ 2627*5088Sab196087 buf = modpath_expand(MSG_ORIG(MSG_STR_MODPATH), 2628*5088Sab196087 origin_root, buf); 2629*5088Sab196087 break; 2630*5088Sab196087 case 'r': 2631*5088Sab196087 cp_str = origin_root; 2632*5088Sab196087 break; 2633*5088Sab196087 case '%': /* %% is reduced to just '%' */ 2634*5088Sab196087 *buf++ = *path; 2635*5088Sab196087 break; 2636*5088Sab196087 default: /* All other % codes are reserved */ 2637*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 2638*5088Sab196087 MSG_INTL(MSG_ERR_BADPATHCODE), *path); 2639*5088Sab196087 /*NOTREACHED*/ 2640*5088Sab196087 break; 2641*5088Sab196087 } 2642*5088Sab196087 if ((cp_str != NULL) && ((len = strlen(cp_str)) > 0)) { 2643*5088Sab196087 bcopy(cp_str, buf, len); 2644*5088Sab196087 buf += len; 2645*5088Sab196087 } 2646*5088Sab196087 } else { /* Non-% character passes straight through */ 2647*5088Sab196087 *buf++ = *path; 2648*5088Sab196087 } 2649*5088Sab196087 } 2650*5088Sab196087 2651*5088Sab196087 *buf = '\0'; 2652*5088Sab196087 return (buf); 2653*5088Sab196087 } 2654*5088Sab196087 2655*5088Sab196087 2656*5088Sab196087 /* 2657*5088Sab196087 * Establish the module search path: state.modpath 2658*5088Sab196087 * 2659*5088Sab196087 * The path used comes from the following sources, taking the first 2660*5088Sab196087 * one that has a value, and ignoring any others: 2661*5088Sab196087 * 2662*5088Sab196087 * - ELFEDIT_PATH environment variable 2663*5088Sab196087 * - -L command line argument 2664*5088Sab196087 * - Default value 2665*5088Sab196087 * 2666*5088Sab196087 * entry: 2667*5088Sab196087 * path - NULL, or the value of the -L command line argument 2668*5088Sab196087 * 2669*5088Sab196087 * exit: 2670*5088Sab196087 * state.modpath has been filled in 2671*5088Sab196087 */ 2672*5088Sab196087 static void 2673*5088Sab196087 establish_modpath(const char *cmdline_path) 2674*5088Sab196087 { 2675*5088Sab196087 char origin_root[PATH_MAX + 1]; /* Where elfedit binary is */ 2676*5088Sab196087 const char *path; /* Initial path */ 2677*5088Sab196087 char *expath; /* Expanded path */ 2678*5088Sab196087 size_t len; 2679*5088Sab196087 char *src, *dst; 2680*5088Sab196087 2681*5088Sab196087 path = getenv(MSG_ORIG(MSG_STR_ENVVAR)); 2682*5088Sab196087 if (path == NULL) 2683*5088Sab196087 path = cmdline_path; 2684*5088Sab196087 if (path == NULL) 2685*5088Sab196087 path = MSG_ORIG(MSG_STR_MODPATH); 2686*5088Sab196087 2687*5088Sab196087 2688*5088Sab196087 /* 2689*5088Sab196087 * Root of tree containing running for running program. 32-bit elfedit 2690*5088Sab196087 * is installed in /usr/bin, and 64-bit elfedit is one level lower 2691*5088Sab196087 * in an ISA-specific subdirectory. So, we find the root by 2692*5088Sab196087 * getting the $ORGIN of the current running program, and trimming 2693*5088Sab196087 * off the last 2 (32-bit) or 3 (64-bit) directories. 2694*5088Sab196087 * 2695*5088Sab196087 * On a standard system, this will simply yield '/'. However, 2696*5088Sab196087 * doing it this way allows us to run elfedit from a proto area, 2697*5088Sab196087 * and pick up modules from the same proto area instead of those 2698*5088Sab196087 * installed on the system. 2699*5088Sab196087 */ 2700*5088Sab196087 if (dlinfo(RTLD_SELF, RTLD_DI_ORIGIN, &origin_root) == -1) 2701*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_CNTGETORIGIN)); 2702*5088Sab196087 len = (sizeof (char *) == 8) ? 3 : 2; 2703*5088Sab196087 src = origin_root + strlen(origin_root); 2704*5088Sab196087 while ((src > origin_root) && (len > 0)) { 2705*5088Sab196087 if (*(src - 1) == '/') 2706*5088Sab196087 len--; 2707*5088Sab196087 src--; 2708*5088Sab196087 } 2709*5088Sab196087 *src = '\0'; 2710*5088Sab196087 2711*5088Sab196087 2712*5088Sab196087 /* 2713*5088Sab196087 * Calculate space needed to hold expanded path. Note that 2714*5088Sab196087 * this assumes that MSG_STR_MODPATH will never contain a '%o' 2715*5088Sab196087 * code, and so, the expansion is not recursive. The codes allowed 2716*5088Sab196087 * are: 2717*5088Sab196087 * %i - ISA of running elfedit (sparc, sparcv9, etc) 2718*5088Sab196087 * %I - 64-bit ISA: Same as %i for 64-bit versions of elfedit, 2719*5088Sab196087 * but yields empty string for 32-bit ISAs. 2720*5088Sab196087 * %o - The original (default) path. 2721*5088Sab196087 * %r - Root of tree holding elfedit program. 2722*5088Sab196087 * %% - A single % 2723*5088Sab196087 * 2724*5088Sab196087 * A % followed by anything else is an error. This allows us to 2725*5088Sab196087 * add new codes in the future without backward compatability issues. 2726*5088Sab196087 */ 2727*5088Sab196087 len = modpath_strlen(path, origin_root); 2728*5088Sab196087 2729*5088Sab196087 expath = elfedit_malloc(MSG_INTL(MSG_ALLOC_EXPATH), len + 1); 2730*5088Sab196087 (void) modpath_expand(path, origin_root, expath); 2731*5088Sab196087 2732*5088Sab196087 /* 2733*5088Sab196087 * Count path segments, eliminate extra '/', and replace ':' 2734*5088Sab196087 * with NULL. 2735*5088Sab196087 */ 2736*5088Sab196087 state.modpath.n = 1; 2737*5088Sab196087 for (src = dst = expath; *src; src++) { 2738*5088Sab196087 if (*src == '/') { 2739*5088Sab196087 switch (*(src + 1)) { 2740*5088Sab196087 case '/': 2741*5088Sab196087 case ':': 2742*5088Sab196087 case '\0': 2743*5088Sab196087 continue; 2744*5088Sab196087 } 2745*5088Sab196087 } 2746*5088Sab196087 if (*src == ':') { 2747*5088Sab196087 state.modpath.n++; 2748*5088Sab196087 *dst = '\0'; 2749*5088Sab196087 } else if (src != dst) { 2750*5088Sab196087 *dst = *src; 2751*5088Sab196087 } 2752*5088Sab196087 dst++; 2753*5088Sab196087 } 2754*5088Sab196087 if (src != dst) 2755*5088Sab196087 *dst = '\0'; 2756*5088Sab196087 2757*5088Sab196087 state.modpath.seg = elfedit_malloc(MSG_INTL(MSG_ALLOC_PATHARR), 2758*5088Sab196087 sizeof (state.modpath.seg[0]) * state.modpath.n); 2759*5088Sab196087 2760*5088Sab196087 src = expath; 2761*5088Sab196087 for (len = 0; len < state.modpath.n; len++) { 2762*5088Sab196087 if (*src == '\0') { 2763*5088Sab196087 state.modpath.seg[len] = MSG_ORIG(MSG_STR_DOT); 2764*5088Sab196087 src++; 2765*5088Sab196087 } else { 2766*5088Sab196087 state.modpath.seg[len] = src; 2767*5088Sab196087 src += strlen(src) + 1; 2768*5088Sab196087 } 2769*5088Sab196087 } 2770*5088Sab196087 } 2771*5088Sab196087 2772*5088Sab196087 /* 2773*5088Sab196087 * When interactive (reading commands from a tty), we catch 2774*5088Sab196087 * SIGINT in order to restart the outer command loop. 2775*5088Sab196087 */ 2776*5088Sab196087 /*ARGSUSED*/ 2777*5088Sab196087 static void 2778*5088Sab196087 sigint_handler(int sig, siginfo_t *sip, void *ucp) 2779*5088Sab196087 { 2780*5088Sab196087 /* Jump to the outer loop to resume */ 2781*5088Sab196087 if (state.msg_jbuf.active) { 2782*5088Sab196087 state.msg_jbuf.active = 0; 2783*5088Sab196087 siglongjmp(state.msg_jbuf.env, 1); 2784*5088Sab196087 } 2785*5088Sab196087 } 2786*5088Sab196087 2787*5088Sab196087 2788*5088Sab196087 static void 2789*5088Sab196087 usage(int full) 2790*5088Sab196087 { 2791*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_BRIEF)); 2792*5088Sab196087 if (full) { 2793*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL1)); 2794*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL2)); 2795*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL3)); 2796*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL4)); 2797*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL5)); 2798*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL6)); 2799*5088Sab196087 elfedit_msg(ELFEDIT_MSG_USAGE, MSG_INTL(MSG_USAGE_DETAIL_LAST)); 2800*5088Sab196087 } 2801*5088Sab196087 elfedit_exit(2); 2802*5088Sab196087 } 2803*5088Sab196087 2804*5088Sab196087 2805*5088Sab196087 /* 2806*5088Sab196087 * In order to complete commands, we need to know about them, 2807*5088Sab196087 * which means that we need to force all the modules to be 2808*5088Sab196087 * loaded. This is a relatively expensive operation, so we use 2809*5088Sab196087 * this function, which avoids doing it more than once in a session. 2810*5088Sab196087 */ 2811*5088Sab196087 static void 2812*5088Sab196087 elfedit_cpl_load_modules(void) 2813*5088Sab196087 { 2814*5088Sab196087 static int loaded; 2815*5088Sab196087 2816*5088Sab196087 if (!loaded) { 2817*5088Sab196087 elfedit_load_modpath(); 2818*5088Sab196087 loaded = 1; /* Don't do it again */ 2819*5088Sab196087 } 2820*5088Sab196087 } 2821*5088Sab196087 2822*5088Sab196087 /* 2823*5088Sab196087 * Compare the token to the given string, and if they share a common 2824*5088Sab196087 * initial sequence, add the tail of string to the tecla command completion 2825*5088Sab196087 * buffer: 2826*5088Sab196087 * 2827*5088Sab196087 * entry: 2828*5088Sab196087 * cpldata - Current completion state 2829*5088Sab196087 * str - String to match against token 2830*5088Sab196087 * casefold - True to allow case insensitive completion, False 2831*5088Sab196087 * if case must match exactly. 2832*5088Sab196087 */ 2833*5088Sab196087 void 2834*5088Sab196087 elfedit_cpl_match(void *cpldata, const char *str, int casefold) 2835*5088Sab196087 { 2836*5088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 2837*5088Sab196087 const char *cont_suffix; 2838*5088Sab196087 const char *type_suffix; 2839*5088Sab196087 2840*5088Sab196087 /* 2841*5088Sab196087 * Reasons to return immediately: 2842*5088Sab196087 * - NULL strings have no completion value 2843*5088Sab196087 * - The string is shorter than the existing item being completed 2844*5088Sab196087 */ 2845*5088Sab196087 if ((str == NULL) || (*str == '\0') || 2846*5088Sab196087 ((cstate->ecpl_token_len != 0) && 2847*5088Sab196087 ((strlen(str) < cstate->ecpl_token_len)))) 2848*5088Sab196087 return; 2849*5088Sab196087 2850*5088Sab196087 /* If the string does not share the existing prefix, don't use it */ 2851*5088Sab196087 if (casefold) { 2852*5088Sab196087 if (strncasecmp(cstate->ecpl_token_str, str, 2853*5088Sab196087 cstate->ecpl_token_len) != 0) 2854*5088Sab196087 return; 2855*5088Sab196087 } else { 2856*5088Sab196087 if (strncmp(cstate->ecpl_token_str, str, 2857*5088Sab196087 cstate->ecpl_token_len) != 0) 2858*5088Sab196087 return; 2859*5088Sab196087 } 2860*5088Sab196087 2861*5088Sab196087 if (cstate->ecpl_add_mod_colon) { 2862*5088Sab196087 cont_suffix = type_suffix = MSG_ORIG(MSG_STR_COLON); 2863*5088Sab196087 } else { 2864*5088Sab196087 cont_suffix = MSG_ORIG(MSG_STR_SPACE); 2865*5088Sab196087 type_suffix = NULL; 2866*5088Sab196087 } 2867*5088Sab196087 (void) cpl_add_completion(cstate->ecpl_cpl, cstate->ecpl_line, 2868*5088Sab196087 cstate->ecpl_word_start, cstate->ecpl_word_end, 2869*5088Sab196087 str + cstate->ecpl_token_len, type_suffix, cont_suffix); 2870*5088Sab196087 2871*5088Sab196087 } 2872*5088Sab196087 2873*5088Sab196087 2874*5088Sab196087 /* 2875*5088Sab196087 * Compare the token to the names of the commands from the given module, 2876*5088Sab196087 * and if they share a common initial sequence, add the tail of string 2877*5088Sab196087 * to the tecla command completion buffer: 2878*5088Sab196087 * 2879*5088Sab196087 * entry: 2880*5088Sab196087 * tok_buf - Token user has entered 2881*5088Sab196087 * tok_len - strlen(tok_buf) 2882*5088Sab196087 * mod - Module definition from which commands should be matched 2883*5088Sab196087 * cpl, line, word_start, word_end, cont_suffix - As documented 2884*5088Sab196087 * for gl_get_line() and cpl_add_completion. 2885*5088Sab196087 */ 2886*5088Sab196087 static void 2887*5088Sab196087 match_module_cmds(ELFEDIT_CPL_STATE *cstate, elfeditGC_module_t *mod) 2888*5088Sab196087 { 2889*5088Sab196087 elfeditGC_cmd_t *cmd; 2890*5088Sab196087 const char **cmd_name; 2891*5088Sab196087 2892*5088Sab196087 for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) 2893*5088Sab196087 for (cmd_name = cmd->cmd_name; *cmd_name; cmd_name++) 2894*5088Sab196087 elfedit_cpl_match(cstate, *cmd_name, 1); 2895*5088Sab196087 } 2896*5088Sab196087 2897*5088Sab196087 2898*5088Sab196087 /* 2899*5088Sab196087 * Compare the token to the known module names, and add those that 2900*5088Sab196087 * match to the list of alternatives via elfedit_cpl_match(). 2901*5088Sab196087 * 2902*5088Sab196087 * entry: 2903*5088Sab196087 * load_all_modules - If True, causes all modules to be loaded 2904*5088Sab196087 * before processing is done. If False, only the modules 2905*5088Sab196087 * currently seen will be used. 2906*5088Sab196087 */ 2907*5088Sab196087 void 2908*5088Sab196087 elfedit_cpl_module(void *cpldata, int load_all_modules) 2909*5088Sab196087 { 2910*5088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 2911*5088Sab196087 MODLIST_T *modlist; 2912*5088Sab196087 2913*5088Sab196087 if (load_all_modules) 2914*5088Sab196087 elfedit_cpl_load_modules(); 2915*5088Sab196087 2916*5088Sab196087 for (modlist = state.modlist; modlist != NULL; 2917*5088Sab196087 modlist = modlist->ml_next) { 2918*5088Sab196087 elfedit_cpl_match(cstate, modlist->ml_mod->mod_name, 1); 2919*5088Sab196087 } 2920*5088Sab196087 } 2921*5088Sab196087 2922*5088Sab196087 2923*5088Sab196087 /* 2924*5088Sab196087 * Compare the token to all the known commands, and add those that 2925*5088Sab196087 * match to the list of alternatives. 2926*5088Sab196087 * 2927*5088Sab196087 * note: 2928*5088Sab196087 * This routine will force modules to be loaded as necessary to 2929*5088Sab196087 * obtain the names it needs to match. 2930*5088Sab196087 */ 2931*5088Sab196087 void 2932*5088Sab196087 elfedit_cpl_command(void *cpldata) 2933*5088Sab196087 { 2934*5088Sab196087 ELFEDIT_CPL_STATE *cstate = (ELFEDIT_CPL_STATE *) cpldata; 2935*5088Sab196087 ELFEDIT_CPL_STATE colon_state; 2936*5088Sab196087 const char *colon_pos; 2937*5088Sab196087 MODLIST_T *modlist; 2938*5088Sab196087 MODLIST_T *insdef; 2939*5088Sab196087 char buf[128]; 2940*5088Sab196087 2941*5088Sab196087 /* 2942*5088Sab196087 * Is there a colon in the command? If so, locate its offset within 2943*5088Sab196087 * the raw input line. 2944*5088Sab196087 */ 2945*5088Sab196087 for (colon_pos = cstate->ecpl_token_str; 2946*5088Sab196087 *colon_pos && (*colon_pos != ':'); colon_pos++) 2947*5088Sab196087 ; 2948*5088Sab196087 2949*5088Sab196087 /* 2950*5088Sab196087 * If no colon was seen, then we are completing a module name, 2951*5088Sab196087 * or one of the commands from 'sys:' 2952*5088Sab196087 */ 2953*5088Sab196087 if (*colon_pos == '\0') { 2954*5088Sab196087 /* 2955*5088Sab196087 * Setting cstate->add_mod_colon tells elfedit_cpl_match() 2956*5088Sab196087 * to add an implicit ':' to the names it matches. We use it 2957*5088Sab196087 * here so the user doesn't have to enter the ':' manually. 2958*5088Sab196087 * Hiding this in the opaque state instead of making it 2959*5088Sab196087 * an argument to that function gives us the ability to 2960*5088Sab196087 * change it later without breaking the published interface. 2961*5088Sab196087 */ 2962*5088Sab196087 cstate->ecpl_add_mod_colon = 1; 2963*5088Sab196087 elfedit_cpl_module(cpldata, 1); 2964*5088Sab196087 cstate->ecpl_add_mod_colon = 0; 2965*5088Sab196087 2966*5088Sab196087 /* Add bare (no sys: prefix) commands from the sys: module */ 2967*5088Sab196087 match_module_cmds(cstate, 2968*5088Sab196087 elfedit_load_module(MSG_ORIG(MSG_MOD_SYS), 1, 0)); 2969*5088Sab196087 2970*5088Sab196087 return; 2971*5088Sab196087 } 2972*5088Sab196087 2973*5088Sab196087 /* 2974*5088Sab196087 * A colon was seen, so we have a module name. Extract the name, 2975*5088Sab196087 * substituting 'sys' for the case where the given name is empty. 2976*5088Sab196087 */ 2977*5088Sab196087 if (colon_pos == 0) 2978*5088Sab196087 (void) strlcpy(buf, MSG_ORIG(MSG_MOD_SYS), sizeof (buf)); 2979*5088Sab196087 else 2980*5088Sab196087 elfedit_strnbcpy(buf, cstate->ecpl_token_str, 2981*5088Sab196087 colon_pos - cstate->ecpl_token_str, sizeof (buf)); 2982*5088Sab196087 2983*5088Sab196087 /* 2984*5088Sab196087 * Locate the module. If it isn't already loaded, make an explicit 2985*5088Sab196087 * attempt to load it and try again. If a module definition is 2986*5088Sab196087 * obtained, process the commands it supplies. 2987*5088Sab196087 */ 2988*5088Sab196087 modlist = module_loaded(buf, &insdef); 2989*5088Sab196087 if (modlist == NULL) { 2990*5088Sab196087 (void) elfedit_load_module(buf, 0, 0); 2991*5088Sab196087 modlist = module_loaded(buf, &insdef); 2992*5088Sab196087 } 2993*5088Sab196087 if (modlist != NULL) { 2994*5088Sab196087 /* 2995*5088Sab196087 * Make a copy of the cstate, and adjust the line and 2996*5088Sab196087 * token so that the new one starts just past the colon 2997*5088Sab196087 * character. We know that the colon exists because 2998*5088Sab196087 * of the preceeding test that found it. Therefore, we do 2999*5088Sab196087 * not need to test against running off the end of the 3000*5088Sab196087 * string here. 3001*5088Sab196087 */ 3002*5088Sab196087 colon_state = *cstate; 3003*5088Sab196087 while (colon_state.ecpl_line[colon_state.ecpl_word_start] != 3004*5088Sab196087 ':') 3005*5088Sab196087 colon_state.ecpl_word_start++; 3006*5088Sab196087 while (*colon_state.ecpl_token_str != ':') { 3007*5088Sab196087 colon_state.ecpl_token_str++; 3008*5088Sab196087 colon_state.ecpl_token_len--; 3009*5088Sab196087 } 3010*5088Sab196087 /* Skip past the ':' character */ 3011*5088Sab196087 colon_state.ecpl_word_start++; 3012*5088Sab196087 colon_state.ecpl_token_str++; 3013*5088Sab196087 colon_state.ecpl_token_len--; 3014*5088Sab196087 3015*5088Sab196087 match_module_cmds(&colon_state, modlist->ml_mod); 3016*5088Sab196087 } 3017*5088Sab196087 } 3018*5088Sab196087 3019*5088Sab196087 3020*5088Sab196087 /* 3021*5088Sab196087 * Command completion function for use with libtacla. 3022*5088Sab196087 */ 3023*5088Sab196087 /*ARGSUSED1*/ 3024*5088Sab196087 static int 3025*5088Sab196087 cmd_match_fcn(WordCompletion *cpl, void *data, const char *line, int word_end) 3026*5088Sab196087 { 3027*5088Sab196087 const char *argv[ELFEDIT_MAXCPLARGS]; 3028*5088Sab196087 ELFEDIT_CPL_STATE cstate; 3029*5088Sab196087 TOK_STATE *tokst; 3030*5088Sab196087 int ndx; 3031*5088Sab196087 int i; 3032*5088Sab196087 elfeditGC_module_t *mod; 3033*5088Sab196087 elfeditGC_cmd_t *cmd; 3034*5088Sab196087 int num_opt; 3035*5088Sab196087 int opt_term_seen; 3036*5088Sab196087 int skip_one; 3037*5088Sab196087 elfedit_cmd_optarg_t *optarg; 3038*5088Sab196087 elfedit_optarg_item_t item; 3039*5088Sab196087 int ostyle_ndx = -1; 3040*5088Sab196087 3041*5088Sab196087 /* 3042*5088Sab196087 * For debugging, enable the following block. It tells the tecla 3043*5088Sab196087 * library that the program using is going to write to stdout. 3044*5088Sab196087 * It will put the tty back into normal mode, and it will cause 3045*5088Sab196087 * tecla to redraw the current input line when it gets control back. 3046*5088Sab196087 */ 3047*5088Sab196087 #if 0 3048*5088Sab196087 gl_normal_io(state.input.gl); 3049*5088Sab196087 #endif 3050*5088Sab196087 3051*5088Sab196087 /* 3052*5088Sab196087 * Tokenize the line up through word_end. The last token in 3053*5088Sab196087 * the list is the one requiring completion. 3054*5088Sab196087 */ 3055*5088Sab196087 tokst = tokenize_user_cmd(line, word_end, 1); 3056*5088Sab196087 if (tokst->tokst_cnt == 0) 3057*5088Sab196087 return (0); 3058*5088Sab196087 3059*5088Sab196087 /* Set up the cstate block, containing the completion state */ 3060*5088Sab196087 ndx = tokst->tokst_cnt - 1; /* Index of token to complete */ 3061*5088Sab196087 cstate.ecpl_cpl = cpl; 3062*5088Sab196087 cstate.ecpl_line = line; 3063*5088Sab196087 cstate.ecpl_word_start = tokst->tokst_buf[ndx].tok_line_off; 3064*5088Sab196087 cstate.ecpl_word_end = word_end; 3065*5088Sab196087 cstate.ecpl_add_mod_colon = 0; 3066*5088Sab196087 cstate.ecpl_token_str = tokst->tokst_buf[ndx].tok_str; 3067*5088Sab196087 cstate.ecpl_token_len = tokst->tokst_buf[ndx].tok_len; 3068*5088Sab196087 3069*5088Sab196087 /* 3070*5088Sab196087 * If there is only one token, then we are completing the 3071*5088Sab196087 * command itself. 3072*5088Sab196087 */ 3073*5088Sab196087 if (ndx == 0) { 3074*5088Sab196087 elfedit_cpl_command(&cstate); 3075*5088Sab196087 return (0); 3076*5088Sab196087 } 3077*5088Sab196087 3078*5088Sab196087 /* 3079*5088Sab196087 * There is more than one token. Use the first one to 3080*5088Sab196087 * locate the definition for the command. If we don't have 3081*5088Sab196087 * a definition for the command, then there's nothing more 3082*5088Sab196087 * we can do. 3083*5088Sab196087 */ 3084*5088Sab196087 cmd = elfedit_find_command(tokst->tokst_buf[0].tok_str, 0, &mod); 3085*5088Sab196087 if (cmd == NULL) 3086*5088Sab196087 return (0); 3087*5088Sab196087 3088*5088Sab196087 /* 3089*5088Sab196087 * Since we know the command, give them a quick usage message. 3090*5088Sab196087 * It may be that they just need a quick reminder about the form 3091*5088Sab196087 * of the command and the options. 3092*5088Sab196087 */ 3093*5088Sab196087 (void) gl_normal_io(state.input.gl); 3094*5088Sab196087 elfedit_printf(MSG_INTL(MSG_USAGE_CMD), 3095*5088Sab196087 elfedit_format_command_usage(mod, cmd, NULL, 0)); 3096*5088Sab196087 3097*5088Sab196087 3098*5088Sab196087 /* 3099*5088Sab196087 * We have a generous setting for ELFEDIT_MAXCPLARGS, so there 3100*5088Sab196087 * should always be plenty of room. If there's not room, we 3101*5088Sab196087 * can't proceed. 3102*5088Sab196087 */ 3103*5088Sab196087 if (ndx >= ELFEDIT_MAXCPLARGS) 3104*5088Sab196087 return (0); 3105*5088Sab196087 3106*5088Sab196087 /* 3107*5088Sab196087 * Put pointers to the tokens into argv, and determine how 3108*5088Sab196087 * many of the tokens are optional arguments. 3109*5088Sab196087 * 3110*5088Sab196087 * We consider the final optional argument to be the rightmost 3111*5088Sab196087 * argument that starts with a '-'. If a '--' is seen, then 3112*5088Sab196087 * we stop there, and any argument that follows is a plain argument 3113*5088Sab196087 * (even if it starts with '-'). 3114*5088Sab196087 * 3115*5088Sab196087 * We look for an inherited '-o' option, because we are willing 3116*5088Sab196087 * to supply command completion for these values. 3117*5088Sab196087 */ 3118*5088Sab196087 num_opt = 0; 3119*5088Sab196087 opt_term_seen = 0; 3120*5088Sab196087 skip_one = 0; 3121*5088Sab196087 for (i = 0; i < ndx; i++) { 3122*5088Sab196087 argv[i] = tokst->tokst_buf[i + 1].tok_str; 3123*5088Sab196087 if (opt_term_seen || skip_one) { 3124*5088Sab196087 skip_one = 0; 3125*5088Sab196087 continue; 3126*5088Sab196087 } 3127*5088Sab196087 skip_one = 0; 3128*5088Sab196087 ostyle_ndx = -1; 3129*5088Sab196087 if ((strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_MINUS)) == NULL) || 3130*5088Sab196087 (*argv[i] != '-')) { 3131*5088Sab196087 opt_term_seen = 1; 3132*5088Sab196087 continue; 3133*5088Sab196087 } 3134*5088Sab196087 num_opt = i + 1; 3135*5088Sab196087 /* 3136*5088Sab196087 * If it is a recognised ELFEDIT_CMDOA_F_VALUE option, 3137*5088Sab196087 * then the item following it is the associated value. 3138*5088Sab196087 * Check for this and skip the value. 3139*5088Sab196087 * 3140*5088Sab196087 * At the same time, look for STDOA_OPT_O inherited 3141*5088Sab196087 * options. We want to identify the index of any such 3142*5088Sab196087 * item. Although the option is simply "-o", we are willing 3143*5088Sab196087 * to treat any option that starts with "-o" as a potential 3144*5088Sab196087 * STDOA_OPT_O. This lets us to command completion for things 3145*5088Sab196087 * like "-onum", and is otherwise harmless, the only cost 3146*5088Sab196087 * being a few additional strcmps by the cpl code. 3147*5088Sab196087 */ 3148*5088Sab196087 if ((optarg = cmd->cmd_opt) == NULL) 3149*5088Sab196087 continue; 3150*5088Sab196087 while (optarg->oa_name != NULL) { 3151*5088Sab196087 int is_ostyle_optarg = 3152*5088Sab196087 (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) && 3153*5088Sab196087 (optarg->oa_name == ELFEDIT_STDOA_OPT_O); 3154*5088Sab196087 3155*5088Sab196087 elfedit_next_optarg(&optarg, &item); 3156*5088Sab196087 if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) { 3157*5088Sab196087 if (is_ostyle_optarg && (strncmp(argv[i], 3158*5088Sab196087 MSG_ORIG(MSG_STR_MINUS_O), 2) == 0)) 3159*5088Sab196087 ostyle_ndx = i + 1; 3160*5088Sab196087 3161*5088Sab196087 if (strcmp(item.oai_name, argv[i]) == 0) { 3162*5088Sab196087 num_opt = i + 2; 3163*5088Sab196087 skip_one = 1; 3164*5088Sab196087 break; 3165*5088Sab196087 } 3166*5088Sab196087 /* 3167*5088Sab196087 * If it didn't match "-o" exactly, but it is 3168*5088Sab196087 * ostyle_ndx, then it is a potential combined 3169*5088Sab196087 * STDOA_OPT_O, as discussed above. It counts 3170*5088Sab196087 * as a single argument. 3171*5088Sab196087 */ 3172*5088Sab196087 if (ostyle_ndx == ndx) 3173*5088Sab196087 break; 3174*5088Sab196087 } 3175*5088Sab196087 } 3176*5088Sab196087 } 3177*5088Sab196087 3178*5088Sab196087 #if 0 3179*5088Sab196087 printf("NDX(%d) NUM_OPT(%d) ostyle_ndx(%d)\n", ndx, num_opt, 3180*5088Sab196087 ostyle_ndx); 3181*5088Sab196087 #endif 3182*5088Sab196087 3183*5088Sab196087 if (ostyle_ndx != -1) { 3184*5088Sab196087 /* 3185*5088Sab196087 * If ostyle_ndx is one less than ndx, and ndx is 3186*5088Sab196087 * the same as num_opt, then we have a definitive 3187*5088Sab196087 * STDOA_OPT_O inherited outstyle option. We supply 3188*5088Sab196087 * the value strings, and are done. 3189*5088Sab196087 */ 3190*5088Sab196087 if ((ostyle_ndx == (ndx - 1)) && (ndx == num_opt)) { 3191*5088Sab196087 elfedit_cpl_atoconst(&cstate, ELFEDIT_CONST_OUTSTYLE); 3192*5088Sab196087 return (0); 3193*5088Sab196087 } 3194*5088Sab196087 3195*5088Sab196087 /* 3196*5088Sab196087 * If ostyle is the same as ndx, then we have an option 3197*5088Sab196087 * staring with "-o" that may end up being a STDOA_OPT_O, 3198*5088Sab196087 * and we are still inside that token. In this case, we 3199*5088Sab196087 * supply completion strings that include the leading 3200*5088Sab196087 * "-o" followed by the values, without a space 3201*5088Sab196087 * (i.e. "-onum"). We then fall through, allowing any 3202*5088Sab196087 * other options starting with "-o" to be added 3203*5088Sab196087 * below. elfedit_cpl_match() will throw out the incorrect 3204*5088Sab196087 * options, so it is harmless to add these extra items in 3205*5088Sab196087 * the worst case, and useful otherwise. 3206*5088Sab196087 */ 3207*5088Sab196087 if (ostyle_ndx == ndx) 3208*5088Sab196087 elfedit_cpl_atoconst(&cstate, 3209*5088Sab196087 ELFEDIT_CONST_OUTSTYLE_MO); 3210*5088Sab196087 } 3211*5088Sab196087 3212*5088Sab196087 /* 3213*5088Sab196087 * If (ndx <= num_opt), then the token needing completion 3214*5088Sab196087 * is an option. If the leading '-' is there, then we should fill 3215*5088Sab196087 * in all of the option alternatives. If anything follows the '-' 3216*5088Sab196087 * though, we assume that the user has already figured out what 3217*5088Sab196087 * option to use, and we leave well enough alone. 3218*5088Sab196087 * 3219*5088Sab196087 * Note that we are intentionally ignoring a related case 3220*5088Sab196087 * where supplying option strings would be legal: In the case 3221*5088Sab196087 * where we are one past the last option (ndx == (num_opt + 1)), 3222*5088Sab196087 * and the current option is an empty string, the argument can 3223*5088Sab196087 * be either a plain argument or an option --- the user needs to 3224*5088Sab196087 * enter the next character before we can tell. It would be 3225*5088Sab196087 * OK to enter the option strings in this case. However, consider 3226*5088Sab196087 * what happens when the first plain argument to the command does 3227*5088Sab196087 * not provide any command completion (e.g. it is a plain integer). 3228*5088Sab196087 * In this case, tecla will see that all the alternatives start 3229*5088Sab196087 * with '-', and will insert a '-' into the input. If the user 3230*5088Sab196087 * intends the next argument to be plain, they will have to delete 3231*5088Sab196087 * this '-', which is annoying. Worse than that, they may be confused 3232*5088Sab196087 * by it, and think that the plain argument is not allowed there. 3233*5088Sab196087 * The best solution is to not supply option strings unless the 3234*5088Sab196087 * user first enters the '-'. 3235*5088Sab196087 */ 3236*5088Sab196087 if ((ndx <= num_opt) && (argv[ndx - 1][0] == '-')) { 3237*5088Sab196087 if ((optarg = cmd->cmd_opt) != NULL) { 3238*5088Sab196087 while (optarg->oa_name != NULL) { 3239*5088Sab196087 elfedit_next_optarg(&optarg, &item); 3240*5088Sab196087 elfedit_cpl_match(&cstate, item.oai_name, 1); 3241*5088Sab196087 } 3242*5088Sab196087 } 3243*5088Sab196087 return (0); 3244*5088Sab196087 } 3245*5088Sab196087 3246*5088Sab196087 /* 3247*5088Sab196087 * At this point we know that ndx and num_opt are not equal. 3248*5088Sab196087 * If num_opt is larger than ndx, then we have an ELFEDIT_CMDOA_F_VALUE 3249*5088Sab196087 * argument at the end, and the following value has not been entered. 3250*5088Sab196087 * 3251*5088Sab196087 * If ndx is greater than num_opt, it means that we are looking 3252*5088Sab196087 * at a plain argument (or in the case where (ndx == (num_opt + 1)), 3253*5088Sab196087 * a *potential* plain argument. 3254*5088Sab196087 * 3255*5088Sab196087 * If the command has a completion function registered, then we 3256*5088Sab196087 * hand off the remaining work to it. The cmd_cplfunc field is 3257*5088Sab196087 * the generic definition. We need to cast it to the type that matches 3258*5088Sab196087 * the proper ELFCLASS before calling it. 3259*5088Sab196087 */ 3260*5088Sab196087 if (state.elf.elfclass == ELFCLASS32) { 3261*5088Sab196087 elfedit32_cmdcpl_func_t *cmdcpl_func = 3262*5088Sab196087 (elfedit32_cmdcpl_func_t *)cmd->cmd_cplfunc; 3263*5088Sab196087 3264*5088Sab196087 if (cmdcpl_func != NULL) 3265*5088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s32, 3266*5088Sab196087 &cstate, ndx, argv, num_opt); 3267*5088Sab196087 } else { 3268*5088Sab196087 elfedit64_cmdcpl_func_t *cmdcpl_func = 3269*5088Sab196087 (elfedit64_cmdcpl_func_t *)cmd->cmd_cplfunc; 3270*5088Sab196087 3271*5088Sab196087 if (cmdcpl_func != NULL) 3272*5088Sab196087 (* cmdcpl_func)(state.elf.obj_state.s64, 3273*5088Sab196087 &cstate, ndx, argv, num_opt); 3274*5088Sab196087 } 3275*5088Sab196087 3276*5088Sab196087 return (0); 3277*5088Sab196087 } 3278*5088Sab196087 3279*5088Sab196087 3280*5088Sab196087 /* 3281*5088Sab196087 * Read a line of input from stdin, and return pointer to it. 3282*5088Sab196087 * 3283*5088Sab196087 * This routine uses a private buffer, so the contents of the returned 3284*5088Sab196087 * string are only good until the next call. 3285*5088Sab196087 */ 3286*5088Sab196087 static const char * 3287*5088Sab196087 read_cmd(void) 3288*5088Sab196087 { 3289*5088Sab196087 char *s; 3290*5088Sab196087 3291*5088Sab196087 if (state.input.full_tty) { 3292*5088Sab196087 state.input.in_tecla = TRUE; 3293*5088Sab196087 s = gl_get_line(state.input.gl, 3294*5088Sab196087 MSG_ORIG(MSG_STR_PROMPT), NULL, -1); 3295*5088Sab196087 state.input.in_tecla = FALSE; 3296*5088Sab196087 /* 3297*5088Sab196087 * gl_get_line() returns NULL for EOF or for error. EOF is fine, 3298*5088Sab196087 * but we need to catch and report anything else. Since 3299*5088Sab196087 * reading from stdin is critical to our operation, an 3300*5088Sab196087 * error implies that we cannot recover and must exit. 3301*5088Sab196087 */ 3302*5088Sab196087 if ((s == NULL) && 3303*5088Sab196087 (gl_return_status(state.input.gl) == GLR_ERROR)) { 3304*5088Sab196087 elfedit_msg(ELFEDIT_MSG_FATAL, MSG_INTL(MSG_ERR_GLREAD), 3305*5088Sab196087 gl_error_message(state.input.gl, NULL, 0)); 3306*5088Sab196087 } 3307*5088Sab196087 } else { 3308*5088Sab196087 /* 3309*5088Sab196087 * This should be a dynamically sized buffer, but for now, 3310*5088Sab196087 * I'm going to take a simpler path. 3311*5088Sab196087 */ 3312*5088Sab196087 static char cmd_buf[ELFEDIT_MAXCMD + 1]; 3313*5088Sab196087 3314*5088Sab196087 s = fgets(cmd_buf, sizeof (cmd_buf), stdin); 3315*5088Sab196087 } 3316*5088Sab196087 3317*5088Sab196087 /* Return user string, or 'quit' on EOF */ 3318*5088Sab196087 return (s ? s : MSG_ORIG(MSG_SYS_CMD_QUIT)); 3319*5088Sab196087 } 3320*5088Sab196087 3321*5088Sab196087 int 3322*5088Sab196087 main(int argc, char **argv, char **envp) 3323*5088Sab196087 { 3324*5088Sab196087 /* 3325*5088Sab196087 * Note: This function can use setjmp()/longjmp() which does 3326*5088Sab196087 * not preserve the values of auto/register variables. Hence, 3327*5088Sab196087 * variables that need their values preserved across a jump must 3328*5088Sab196087 * be marked volatile, or must not be auto/register. 3329*5088Sab196087 * 3330*5088Sab196087 * Volatile can be messy, because it requires explictly casting 3331*5088Sab196087 * away the attribute when passing it to functions, or declaring 3332*5088Sab196087 * those functions with the attribute as well. In a single threaded 3333*5088Sab196087 * program like this one, an easier approach is to make things 3334*5088Sab196087 * static. That can be done here, or by putting things in the 3335*5088Sab196087 * 'state' structure. 3336*5088Sab196087 */ 3337*5088Sab196087 3338*5088Sab196087 int c, i; 3339*5088Sab196087 int num_batch = 0; 3340*5088Sab196087 char **batch_list = NULL; 3341*5088Sab196087 const char *modpath = NULL; 3342*5088Sab196087 3343*5088Sab196087 /* 3344*5088Sab196087 * Always have liblddb display unclipped section names. 3345*5088Sab196087 * This global is exported by liblddb, and declared in debug.h. 3346*5088Sab196087 */ 3347*5088Sab196087 dbg_desc->d_extra |= DBG_E_LONG; 3348*5088Sab196087 3349*5088Sab196087 opterr = 0; 3350*5088Sab196087 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) { 3351*5088Sab196087 switch (c) { 3352*5088Sab196087 case 'a': 3353*5088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT; 3354*5088Sab196087 break; 3355*5088Sab196087 3356*5088Sab196087 case 'd': 3357*5088Sab196087 state.flags |= ELFEDIT_F_DEBUG; 3358*5088Sab196087 break; 3359*5088Sab196087 3360*5088Sab196087 case 'e': 3361*5088Sab196087 /* 3362*5088Sab196087 * Delay parsing the -e options until after the call to 3363*5088Sab196087 * conv_check_native() so that we won't bother loading 3364*5088Sab196087 * modules of the wrong class. 3365*5088Sab196087 */ 3366*5088Sab196087 if (batch_list == NULL) 3367*5088Sab196087 batch_list = elfedit_malloc( 3368*5088Sab196087 MSG_INTL(MSG_ALLOC_BATCHLST), 3369*5088Sab196087 sizeof (*batch_list) * (argc - 1)); 3370*5088Sab196087 batch_list[num_batch++] = optarg; 3371*5088Sab196087 break; 3372*5088Sab196087 3373*5088Sab196087 case 'L': 3374*5088Sab196087 modpath = optarg; 3375*5088Sab196087 break; 3376*5088Sab196087 3377*5088Sab196087 case 'o': 3378*5088Sab196087 if (elfedit_atooutstyle(optarg, &state.outstyle) == 0) 3379*5088Sab196087 usage(1); 3380*5088Sab196087 break; 3381*5088Sab196087 3382*5088Sab196087 case 'r': 3383*5088Sab196087 state.flags |= ELFEDIT_F_READONLY; 3384*5088Sab196087 break; 3385*5088Sab196087 3386*5088Sab196087 case '?': 3387*5088Sab196087 usage(1); 3388*5088Sab196087 } 3389*5088Sab196087 } 3390*5088Sab196087 3391*5088Sab196087 /* 3392*5088Sab196087 * We allow 0, 1, or 2 files: 3393*5088Sab196087 * 3394*5088Sab196087 * The no-file case is an extremely limited mode, in which the 3395*5088Sab196087 * only commands allowed to execute come from the sys: module. 3396*5088Sab196087 * This mode exists primarily to allow easy access to the help 3397*5088Sab196087 * facility. 3398*5088Sab196087 * 3399*5088Sab196087 * To get full access to elfedit's capablities, there must 3400*5088Sab196087 * be an input file. If this is not a readonly 3401*5088Sab196087 * session, then an optional second output file is allowed. 3402*5088Sab196087 * 3403*5088Sab196087 * In the case where two files are given and the session is 3404*5088Sab196087 * readonly, use a full usage message, because the simple 3405*5088Sab196087 * one isn't enough for the user to understand their error. 3406*5088Sab196087 * Otherwise, the simple usage message suffices. 3407*5088Sab196087 */ 3408*5088Sab196087 argc = argc - optind; 3409*5088Sab196087 if ((argc == 2) && (state.flags & ELFEDIT_F_READONLY)) 3410*5088Sab196087 usage(1); 3411*5088Sab196087 if (argc > 2) 3412*5088Sab196087 usage(0); 3413*5088Sab196087 3414*5088Sab196087 state.file.present = (argc != 0); 3415*5088Sab196087 3416*5088Sab196087 /* 3417*5088Sab196087 * If we have a file to edit, and unless told otherwise by the 3418*5088Sab196087 * caller, we try to run the 64-bit version of this program 3419*5088Sab196087 * when the system is capable of it. If that fails, then we 3420*5088Sab196087 * continue on with the currently running version. 3421*5088Sab196087 * 3422*5088Sab196087 * To force 32-bit execution on a 64-bit host, set the 3423*5088Sab196087 * LD_NOEXEC_64 environment variable to a non-empty value. 3424*5088Sab196087 * 3425*5088Sab196087 * There is no reason to bother with this if in "no file" mode. 3426*5088Sab196087 */ 3427*5088Sab196087 if (state.file.present != 0) 3428*5088Sab196087 (void) conv_check_native(argv, envp); 3429*5088Sab196087 3430*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_VERSION), 3431*5088Sab196087 (sizeof (char *) == 8) ? 64 : 32); 3432*5088Sab196087 3433*5088Sab196087 /* 3434*5088Sab196087 * Put a module definition for the builtin system module on the 3435*5088Sab196087 * module list. We know it starts out empty, so we do not have 3436*5088Sab196087 * to go through a more general insertion process than this. 3437*5088Sab196087 */ 3438*5088Sab196087 state.modlist = elfedit_sys_init(ELFEDIT_VER_CURRENT); 3439*5088Sab196087 3440*5088Sab196087 /* Establish the search path for loadable modules */ 3441*5088Sab196087 establish_modpath(modpath); 3442*5088Sab196087 3443*5088Sab196087 /* 3444*5088Sab196087 * Now that we are running the final version of this program, 3445*5088Sab196087 * deal with the input/output file(s). 3446*5088Sab196087 */ 3447*5088Sab196087 if (state.file.present == 0) { 3448*5088Sab196087 /* 3449*5088Sab196087 * This is arbitrary --- we simply need to be able to 3450*5088Sab196087 * load modules so that we can access their help strings 3451*5088Sab196087 * and command completion functions. Without a file, we 3452*5088Sab196087 * will refuse to call commands from any module other 3453*5088Sab196087 * than sys. Those commands have been written to be aware 3454*5088Sab196087 * of the case where there is no input file, and are 3455*5088Sab196087 * therefore safe to run. 3456*5088Sab196087 */ 3457*5088Sab196087 state.elf.elfclass = ELFCLASS32; 3458*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_NOFILE)); 3459*5088Sab196087 3460*5088Sab196087 } else { 3461*5088Sab196087 state.file.infile = argv[optind]; 3462*5088Sab196087 if (argc == 1) { 3463*5088Sab196087 state.file.outfile = state.file.infile; 3464*5088Sab196087 if (state.flags & ELFEDIT_F_READONLY) 3465*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 3466*5088Sab196087 MSG_INTL(MSG_DEBUG_READONLY)); 3467*5088Sab196087 else 3468*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 3469*5088Sab196087 MSG_INTL(MSG_DEBUG_INPLACEWARN), 3470*5088Sab196087 state.file.infile); 3471*5088Sab196087 } else { 3472*5088Sab196087 state.file.outfile = argv[optind + 1]; 3473*5088Sab196087 create_outfile(state.file.infile, state.file.outfile); 3474*5088Sab196087 elfedit_msg(ELFEDIT_MSG_DEBUG, 3475*5088Sab196087 MSG_INTL(MSG_DEBUG_CPFILE), 3476*5088Sab196087 state.file.infile, state.file.outfile); 3477*5088Sab196087 /* 3478*5088Sab196087 * We are editing a copy of the original file that we 3479*5088Sab196087 * just created. If we should exit before the edits are 3480*5088Sab196087 * updated, then we want to unlink this copy so that we 3481*5088Sab196087 * don't leave junk lying around. Once an update 3482*5088Sab196087 * succeeds however, we'll leave it in place even 3483*5088Sab196087 * if an error occurs afterwards. 3484*5088Sab196087 */ 3485*5088Sab196087 state.file.unlink_on_exit = 1; 3486*5088Sab196087 optind++; /* Edit copy instead of the original */ 3487*5088Sab196087 } 3488*5088Sab196087 3489*5088Sab196087 init_obj_state(state.file.outfile); 3490*5088Sab196087 } 3491*5088Sab196087 3492*5088Sab196087 3493*5088Sab196087 /* 3494*5088Sab196087 * Process commands. 3495*5088Sab196087 * 3496*5088Sab196087 * If any -e options were used, then do them and 3497*5088Sab196087 * immediately exit. On error, exit immediately without 3498*5088Sab196087 * updating the target ELF file. On success, the 'write' 3499*5088Sab196087 * and 'quit' commands are implicit in this mode. 3500*5088Sab196087 * 3501*5088Sab196087 * If no -e options are used, read commands from stdin. 3502*5088Sab196087 * quit must be explicitly used. Exit is implicit on EOF. 3503*5088Sab196087 * If stdin is a tty, then errors do not cause the editor 3504*5088Sab196087 * to terminate. Rather, the error message is printed, and the 3505*5088Sab196087 * user prompted to continue. 3506*5088Sab196087 */ 3507*5088Sab196087 if (batch_list != NULL) { /* -e was used */ 3508*5088Sab196087 /* Compile the commands */ 3509*5088Sab196087 for (i = 0; i < num_batch; i++) 3510*5088Sab196087 parse_user_cmd(batch_list[i]); 3511*5088Sab196087 free(batch_list); 3512*5088Sab196087 3513*5088Sab196087 /* 3514*5088Sab196087 * 'write' and 'quit' are implicit in this mode. 3515*5088Sab196087 * Add them as well. 3516*5088Sab196087 */ 3517*5088Sab196087 if ((state.flags & ELFEDIT_F_READONLY) == 0) 3518*5088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_WRITE)); 3519*5088Sab196087 parse_user_cmd(MSG_ORIG(MSG_SYS_CMD_QUIT)); 3520*5088Sab196087 3521*5088Sab196087 /* And run them. This won't return, thanks to the 'quit' */ 3522*5088Sab196087 dispatch_user_cmds(); 3523*5088Sab196087 } else { 3524*5088Sab196087 state.input.is_tty = isatty(fileno(stdin)); 3525*5088Sab196087 state.input.full_tty = state.input.is_tty && 3526*5088Sab196087 isatty(fileno(stdout)); 3527*5088Sab196087 3528*5088Sab196087 if (state.input.full_tty) { 3529*5088Sab196087 struct sigaction act; 3530*5088Sab196087 3531*5088Sab196087 act.sa_sigaction = sigint_handler; 3532*5088Sab196087 (void) sigemptyset(&act.sa_mask); 3533*5088Sab196087 act.sa_flags = 0; 3534*5088Sab196087 if (sigaction(SIGINT, &act, NULL) == -1) { 3535*5088Sab196087 int err = errno; 3536*5088Sab196087 elfedit_msg(ELFEDIT_MSG_ERR, 3537*5088Sab196087 MSG_INTL(MSG_ERR_SIGACTION), strerror(err)); 3538*5088Sab196087 } 3539*5088Sab196087 /* 3540*5088Sab196087 * If pager process exits before we are done 3541*5088Sab196087 * writing, we can see SIGPIPE. Prevent it 3542*5088Sab196087 * from killing the process. 3543*5088Sab196087 */ 3544*5088Sab196087 (void) sigignore(SIGPIPE); 3545*5088Sab196087 3546*5088Sab196087 /* Open tecla handle for command line editing */ 3547*5088Sab196087 state.input.gl = new_GetLine(ELFEDIT_MAXCMD, 3548*5088Sab196087 ELFEDIT_MAXHIST); 3549*5088Sab196087 /* Register our command completion function */ 3550*5088Sab196087 (void) gl_customize_completion(state.input.gl, 3551*5088Sab196087 NULL, cmd_match_fcn); 3552*5088Sab196087 3553*5088Sab196087 /* 3554*5088Sab196087 * Make autoprint the default for interactive 3555*5088Sab196087 * sessions. 3556*5088Sab196087 */ 3557*5088Sab196087 state.flags |= ELFEDIT_F_AUTOPRINT; 3558*5088Sab196087 } 3559*5088Sab196087 for (;;) { 3560*5088Sab196087 /* 3561*5088Sab196087 * If this is an interactive session, then use 3562*5088Sab196087 * sigsetjmp()/siglongjmp() to recover from bad 3563*5088Sab196087 * commands and keep going. A non-0 return from 3564*5088Sab196087 * sigsetjmp() means that an error just occurred. 3565*5088Sab196087 * In that case, we simply restart this loop. 3566*5088Sab196087 */ 3567*5088Sab196087 if (state.input.is_tty) { 3568*5088Sab196087 if (sigsetjmp(state.msg_jbuf.env, 1) != 0) { 3569*5088Sab196087 if (state.input.full_tty) 3570*5088Sab196087 gl_abandon_line(state.input.gl); 3571*5088Sab196087 continue; 3572*5088Sab196087 } 3573*5088Sab196087 state.msg_jbuf.active = TRUE; 3574*5088Sab196087 } 3575*5088Sab196087 3576*5088Sab196087 /* 3577*5088Sab196087 * Force all output out before each command. 3578*5088Sab196087 * This is a no-OP when a tty is in use, but 3579*5088Sab196087 * in a pipeline, it ensures that the block 3580*5088Sab196087 * mode buffering doesn't delay output past 3581*5088Sab196087 * the completion of each command. 3582*5088Sab196087 * 3583*5088Sab196087 * If we didn't do this, the output would eventually 3584*5088Sab196087 * arrive at its destination, but the lag can be 3585*5088Sab196087 * annoying when you pipe the output into a tool 3586*5088Sab196087 * that displays the results in real time. 3587*5088Sab196087 */ 3588*5088Sab196087 (void) fflush(stdout); 3589*5088Sab196087 (void) fflush(stderr); 3590*5088Sab196087 3591*5088Sab196087 parse_user_cmd(read_cmd()); 3592*5088Sab196087 dispatch_user_cmds(); 3593*5088Sab196087 state.msg_jbuf.active = FALSE; 3594*5088Sab196087 } 3595*5088Sab196087 } 3596*5088Sab196087 3597*5088Sab196087 3598*5088Sab196087 /*NOTREACHED*/ 3599*5088Sab196087 return (0); 3600*5088Sab196087 } 3601