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 (cmd_info->opt_flgs & 140 (d_FLAG | m_FLAG | p_FLAG | s_FLAG | t_FLAG | x_FLAG)) || 141 ((cmd_info->opt_flgs & r_FLAG) && 142 (cmd_info->opt_flgs & (a_FLAG | b_FLAG)))) { 143 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_AR), 144 cmd_info->arnam); 145 exit(1); 146 } 147 148 (*cmd_info->comfun)(cmd_info); 149 if (cmd_info->modified) { 150 writefile(cmd_info); 151 } else 152 (void) close(fd); 153 154 ret = notfound(cmd_info); 155 156 /* 157 * Check SWAP 158 */ 159 if (cmd_info->opt_flgs & z_FLAG) 160 check_swap(); 161 162 free(new); 163 free(cmd_info); 164 return (ret); 165 166 } 167 168 /* 169 * Option handing function. 170 * Using getopt(), following xcu4 convention. 171 */ 172 static void 173 setup(int argc, char *argv[], Cmd_info *cmd_info) 174 { 175 int Vflag = 0; 176 int c; 177 int usage_err = 0; 178 179 while ((c = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != -1) { 180 switch (c) { 181 case 'a': /* position after named archive member file */ 182 cmd_info->opt_flgs |= a_FLAG; 183 cmd_info->ponam = trim(optarg); 184 break; 185 case 'b': /* position before named archive member file */ 186 case 'i': /* position before named archive member: same as b */ 187 cmd_info->opt_flgs |= b_FLAG; 188 cmd_info->ponam = trim(optarg); 189 break; 190 case 'c': /* supress messages */ 191 cmd_info->opt_flgs |= c_FLAG; 192 break; 193 case 'd': 194 /* 195 * key operation: 196 * delete files from the archive 197 */ 198 setcom(cmd_info, dcmd); 199 cmd_info->opt_flgs |= d_FLAG; 200 break; 201 case 'l': /* ignored */ 202 break; 203 case 'm': 204 /* 205 * key operation: 206 * move files to end of the archive 207 * or as indicated by position flag 208 */ 209 setcom(cmd_info, mcmd); 210 cmd_info->opt_flgs |= m_FLAG; 211 break; 212 case 'p': 213 /* 214 * key operation: 215 * print files in the archive 216 */ 217 setcom(cmd_info, pcmd); 218 cmd_info->opt_flgs |= p_FLAG; 219 break; 220 case 'q': 221 /* 222 * key operation: 223 * quickly append files to end of the archive 224 */ 225 setcom(cmd_info, qcmd); 226 cmd_info->opt_flgs |= q_FLAG; 227 break; 228 case 'r': 229 /* 230 * key operation: 231 * replace or add files to the archive 232 */ 233 setcom(cmd_info, rcmd); 234 cmd_info->opt_flgs |= r_FLAG; 235 break; 236 case 's': /* force symbol table regeneration */ 237 cmd_info->opt_flgs |= s_FLAG; 238 break; 239 case 'S': /* Build SYM64 symbol table */ 240 cmd_info->opt_flgs |= S_FLAG; 241 break; 242 case 't': 243 /* 244 * key operation: 245 * print table of contents 246 */ 247 setcom(cmd_info, tcmd); 248 cmd_info->opt_flgs |= t_FLAG; 249 break; 250 case 'u': /* update: change archive dependent on file dates */ 251 cmd_info->opt_flgs |= u_FLAG; 252 break; 253 case 'v': /* verbose */ 254 cmd_info->opt_flgs |= v_FLAG; 255 break; 256 case 'x': 257 /* 258 * key operation: 259 * extract files from the archive 260 */ 261 setcom(cmd_info, xcmd); 262 cmd_info->opt_flgs |= x_FLAG; 263 break; 264 case 'z': 265 cmd_info->opt_flgs |= z_FLAG; 266 break; 267 case 'V': 268 /* 269 * print version information. 270 * adjust command line access accounting 271 */ 272 if (Vflag == 0) { 273 (void) fprintf(stderr, 274 MSG_ORIG(MSG_FMT_VERSION), 275 (const char *)SGU_PKG, 276 (const char *)SGU_REL); 277 Vflag++; 278 } 279 break; 280 case 'C': 281 cmd_info->opt_flgs |= C_FLAG; 282 break; 283 case 'M': 284 /* 285 * -M was an original undocumented AT&T feature that 286 * would force the use of mmap() instead of read() 287 * for pulling file data into the process before 288 * writing it to the archive. Ignored. 289 */ 290 break; 291 case 'T': 292 cmd_info->opt_flgs |= T_FLAG; 293 break; 294 case ':': 295 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_02), optopt); 296 usage_err++; 297 break; 298 case '?': 299 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_03), optopt); 300 usage_err++; 301 break; 302 } 303 } 304 305 if (usage_err || argc - optind < 1) 306 usage(); 307 308 cmd_info->arnam = argv[optind]; 309 cmd_info->namv = &argv[optind+1]; 310 cmd_info->namc = argc - optind - 1; 311 312 /* 313 * GNU ar popularized the use of -s on its own which previously used to 314 * require another command function. As such, we don't set a command 315 * function when we encounter the -s flag because that might otherwise 316 * clobber an existing one being set and would interrupt the detection 317 * of multiple flags being used that way. 318 * 319 * If after processing everything, we find there's no command function 320 * set and the -s flag has been set, then we can finally set a command 321 * function. The command function for -t 'tcmd' is used in this case. It 322 * knows to only print out data if -t has been specified. 323 * 324 * While ar has not traditionally been very stringent about using flags 325 * in circumstances they aren't called for, we go ahead and check for 326 * that now for this newer option. 327 */ 328 if (cmd_info->comfun == NULL) { 329 if ((cmd_info->opt_flgs & s_FLAG) != 0) { 330 if ((cmd_info->opt_flgs & ~(s_FLAG | v_FLAG)) != 0) { 331 (void) fprintf(stderr, 332 MSG_INTL(MSG_USAGE_S_BAD_ARG)); 333 exit(1); 334 } 335 336 if (cmd_info->namc > 1) { 337 (void) fprintf(stderr, 338 MSG_INTL(MSG_USAGE_S_EXTRA_AR)); 339 exit(1); 340 } 341 342 setcom(cmd_info, tcmd); 343 } else if ((cmd_info->opt_flgs & (d_FLAG | r_FLAG | q_FLAG | 344 s_FLAG | t_FLAG | p_FLAG | m_FLAG | x_FLAG)) == 0) { 345 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_01)); 346 exit(1); 347 } 348 } 349 } 350 351 352 /* 353 * Set the function to be called to do the key operation. 354 * Check that only one key is indicated. 355 */ 356 static void 357 setcom(Cmd_info *cmd_info, Cmd_func *fun) 358 { 359 if (cmd_info->comfun != 0) { 360 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_04)); 361 exit(1); 362 } 363 cmd_info->comfun = fun; 364 } 365 366 static void 367 usage(void) 368 { 369 (void) fprintf(stderr, MSG_INTL(MSG_USAGE)); 370 exit(1); 371 } 372 373 /*ARGSUSED0*/ 374 static void 375 sigexit(int sig) 376 { 377 exit(100); 378 } 379 380 /* tells the user which of the listed files were not found in the archive */ 381 382 static int 383 notfound(Cmd_info *cmd_info) 384 { 385 int i, n; 386 387 n = 0; 388 for (i = 0; i < cmd_info->namc; i++) 389 if (cmd_info->namv[i]) { 390 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_FILE), 391 cmd_info->namv[i]); 392 n++; 393 } 394 return (n); 395 } 396 397 /* 398 * Debugging info 399 */ 400 static void 401 check_swap(void) 402 { 403 (void) system(MSG_ORIG(MSG_CMD_SWAP)); 404 } 405