1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1988 AT&T */ 22 /* All Rights Reserved */ 23 24 /* 25 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. 26 */ 27 28 /* 29 * Copyright (c) 2018, Joyent, Inc. 30 * Copyright 2021 Oxide Computer Company 31 */ 32 33 #include "inc.h" 34 #include "conv.h" 35 36 /* 37 * Forward declarations 38 */ 39 static void setup(int, char **, Cmd_info *); 40 static void setcom(Cmd_info *, Cmd_func); 41 static void usage(void); 42 static void sigexit(int sig); 43 static int notfound(Cmd_info *); 44 static void check_swap(); 45 46 const char * 47 _ar_msg(Msg mid) 48 { 49 return (gettext(MSG_ORIG(mid))); 50 } 51 52 53 void 54 establish_sighandler(void (*handler)()) 55 { 56 static const int signum[] = {SIGHUP, SIGINT, SIGQUIT, 0}; 57 int i; 58 59 if (handler == SIG_IGN) { 60 /* Ignore all the specified signals */ 61 for (i = 0; signum[i]; i++) 62 (void) signal(signum[i], SIG_IGN); 63 64 } else { 65 /* 66 * Set any signal that doesn't default to being ignored 67 * to our signal handler. 68 */ 69 for (i = 0; signum[i]; i++) 70 if (signal(signum[i], SIG_IGN) != SIG_IGN) 71 (void) signal(signum[i], handler); 72 } 73 } 74 75 int 76 main(int argc, char **argv, char *envp[]) 77 { 78 int fd; 79 Cmd_info *cmd_info; 80 int ret; 81 char *new = NULL; 82 83 #ifndef XPG4 84 /* 85 * Check for a binary that better fits this architecture. 86 */ 87 (void) conv_check_native(argv, envp); 88 #endif 89 90 /* 91 * Establish locale. 92 */ 93 (void) setlocale(LC_ALL, MSG_ORIG(MSG_STR_EMPTY)); 94 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 95 96 /* Allow a graceful exit up until we start to write an archive */ 97 establish_sighandler(sigexit); 98 99 /* 100 * Initialize cmd_info 101 */ 102 cmd_info = (Cmd_info *)calloc(1, sizeof (Cmd_info)); 103 if (cmd_info == NULL) { 104 int err = errno; 105 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC), strerror(err)); 106 exit(1); 107 } 108 109 if (argc < 2) 110 usage(); 111 112 /* 113 * Option handling. 114 */ 115 if (argv[1][0] != '-') { 116 new = (char *)malloc(strlen(argv[1]) + 2); 117 if (new == NULL) { 118 int err = errno; 119 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC), 120 strerror(err)); 121 exit(1); 122 } 123 (void) strcpy(new, MSG_ORIG(MSG_STR_HYPHEN)); 124 (void) strcat(new, argv[1]); 125 argv[1] = new; 126 } 127 setup(argc, argv, cmd_info); 128 129 /* 130 * Check SWAP 131 */ 132 if (cmd_info->opt_flgs & z_FLAG) 133 check_swap(); 134 135 cmd_info->modified = (cmd_info->opt_flgs & s_FLAG); 136 fd = getaf(cmd_info); 137 138 if (fd == -1) { 139 boolean_t req_arg = (cmd_info->opt_flgs & (d_FLAG | m_FLAG | 140 p_FLAG | t_FLAG | x_FLAG)) != 0; 141 boolean_t req_r = (cmd_info->opt_flgs & r_FLAG) && 142 (cmd_info->opt_flgs & (a_FLAG | b_FLAG)); 143 boolean_t req_s = (cmd_info->opt_flgs & s_FLAG) && 144 (cmd_info->opt_flgs & r_FLAG) == 0; 145 146 if (req_arg || req_r || req_s) { 147 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_AR), 148 cmd_info->arnam); 149 exit(1); 150 } 151 } 152 153 (*cmd_info->comfun)(cmd_info); 154 if (cmd_info->modified) { 155 writefile(cmd_info); 156 } else 157 (void) close(fd); 158 159 ret = notfound(cmd_info); 160 161 /* 162 * Check SWAP 163 */ 164 if (cmd_info->opt_flgs & z_FLAG) 165 check_swap(); 166 167 free(new); 168 free(cmd_info); 169 return (ret); 170 171 } 172 173 /* 174 * Option handing function. 175 * Using getopt(), following xcu4 convention. 176 */ 177 static void 178 setup(int argc, char *argv[], Cmd_info *cmd_info) 179 { 180 int Vflag = 0; 181 int c; 182 int usage_err = 0; 183 184 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) { 185 switch (c) { 186 case 'a': /* position after named archive member file */ 187 cmd_info->opt_flgs |= a_FLAG; 188 cmd_info->ponam = trim(optarg); 189 break; 190 case 'b': /* position before named archive member file */ 191 case 'i': /* position before named archive member: same as b */ 192 cmd_info->opt_flgs |= b_FLAG; 193 cmd_info->ponam = trim(optarg); 194 break; 195 case 'c': /* supress messages */ 196 cmd_info->opt_flgs |= c_FLAG; 197 break; 198 case 'd': 199 /* 200 * key operation: 201 * delete files from the archive 202 */ 203 setcom(cmd_info, dcmd); 204 cmd_info->opt_flgs |= d_FLAG; 205 break; 206 case 'l': /* ignored */ 207 break; 208 case 'm': 209 /* 210 * key operation: 211 * move files to end of the archive 212 * or as indicated by position flag 213 */ 214 setcom(cmd_info, mcmd); 215 cmd_info->opt_flgs |= m_FLAG; 216 break; 217 case 'p': 218 /* 219 * key operation: 220 * print files in the archive 221 */ 222 setcom(cmd_info, pcmd); 223 cmd_info->opt_flgs |= p_FLAG; 224 break; 225 case 'q': 226 /* 227 * key operation: 228 * quickly append files to end of the archive 229 */ 230 setcom(cmd_info, qcmd); 231 cmd_info->opt_flgs |= q_FLAG; 232 break; 233 case 'r': 234 /* 235 * key operation: 236 * replace or add files to the archive 237 */ 238 setcom(cmd_info, rcmd); 239 cmd_info->opt_flgs |= r_FLAG; 240 break; 241 case 's': /* force symbol table regeneration */ 242 cmd_info->opt_flgs |= s_FLAG; 243 break; 244 case 'S': /* Build SYM64 symbol table */ 245 cmd_info->opt_flgs |= S_FLAG; 246 break; 247 case 't': 248 /* 249 * key operation: 250 * print table of contents 251 */ 252 setcom(cmd_info, tcmd); 253 cmd_info->opt_flgs |= t_FLAG; 254 break; 255 case 'u': /* update: change archive dependent on file dates */ 256 cmd_info->opt_flgs |= u_FLAG; 257 break; 258 case 'v': /* verbose */ 259 cmd_info->opt_flgs |= v_FLAG; 260 break; 261 case 'x': 262 /* 263 * key operation: 264 * extract files from the archive 265 */ 266 setcom(cmd_info, xcmd); 267 cmd_info->opt_flgs |= x_FLAG; 268 break; 269 case 'z': 270 cmd_info->opt_flgs |= z_FLAG; 271 break; 272 case 'V': 273 /* 274 * print version information. 275 * adjust command line access accounting 276 */ 277 if (Vflag == 0) { 278 (void) fprintf(stderr, 279 MSG_ORIG(MSG_FMT_VERSION), 280 (const char *)SGU_PKG, 281 (const char *)SGU_REL); 282 Vflag++; 283 } 284 break; 285 case 'C': 286 cmd_info->opt_flgs |= C_FLAG; 287 break; 288 case 'M': 289 /* 290 * -M was an original undocumented AT&T feature that 291 * would force the use of mmap() instead of read() 292 * for pulling file data into the process before 293 * writing it to the archive. Ignored. 294 */ 295 break; 296 case 'T': 297 cmd_info->opt_flgs |= T_FLAG; 298 break; 299 case ':': 300 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPERAND), 301 optopt); 302 usage_err++; 303 break; 304 case '?': 305 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_OPTION), 306 optopt); 307 usage_err++; 308 break; 309 } 310 } 311 312 if (usage_err || argc - optind < 1) 313 usage(); 314 315 cmd_info->arnam = argv[optind]; 316 cmd_info->namv = &argv[optind+1]; 317 cmd_info->namc = argc - optind - 1; 318 319 /* 320 * GNU ar popularized the use of -s on its own which previously used to 321 * require another command function. As such, we don't set a command 322 * function when we encounter the -s flag because that might otherwise 323 * clobber an existing one being set and would interrupt the detection 324 * of multiple flags being used that way. 325 * 326 * If after processing everything, we find there's no command function 327 * set and the -s flag has been set, then we can finally set a command 328 * function. The command function for -t 'tcmd' is used in this case. It 329 * knows to only print out data if -t has been specified. 330 * 331 * While ar has not traditionally been very stringent about using flags 332 * in circumstances they aren't called for, we go ahead and check for 333 * that now for this newer option. 334 */ 335 if (cmd_info->comfun == NULL) { 336 if ((cmd_info->opt_flgs & s_FLAG) != 0) { 337 if ((cmd_info->opt_flgs & ~(s_FLAG | v_FLAG)) != 0) { 338 (void) fprintf(stderr, 339 MSG_INTL(MSG_USAGE_S_BAD_ARG)); 340 exit(1); 341 } 342 343 if (cmd_info->namc > 0) { 344 (void) fprintf(stderr, 345 MSG_INTL(MSG_USAGE_S_EXTRA_AR)); 346 exit(1); 347 } 348 349 setcom(cmd_info, tcmd); 350 } else if ((cmd_info->opt_flgs & (d_FLAG | r_FLAG | q_FLAG | 351 s_FLAG | t_FLAG | p_FLAG | m_FLAG | x_FLAG)) == 0) { 352 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_REQ_FLAG)); 353 exit(1); 354 } 355 } 356 } 357 358 359 /* 360 * Set the function to be called to do the key operation. 361 * Check that only one key is indicated. 362 */ 363 static void 364 setcom(Cmd_info *cmd_info, Cmd_func *fun) 365 { 366 if (cmd_info->comfun != NULL) { 367 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_TOO_MANY)); 368 exit(1); 369 } 370 cmd_info->comfun = fun; 371 } 372 373 static void 374 usage(void) 375 { 376 (void) fprintf(stderr, MSG_INTL(MSG_USAGE)); 377 exit(1); 378 } 379 380 /*ARGSUSED0*/ 381 static void 382 sigexit(int sig) 383 { 384 exit(100); 385 } 386 387 /* tells the user which of the listed files were not found in the archive */ 388 389 static int 390 notfound(Cmd_info *cmd_info) 391 { 392 int i, n; 393 394 n = 0; 395 for (i = 0; i < cmd_info->namc; i++) 396 if (cmd_info->namv[i]) { 397 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_FILE), 398 cmd_info->namv[i]); 399 n++; 400 } 401 return (n); 402 } 403 404 /* 405 * Debugging info 406 */ 407 static void 408 check_swap(void) 409 { 410 (void) system(MSG_ORIG(MSG_CMD_SWAP)); 411 } 412