xref: /illumos-gate/usr/src/cmd/sgs/ar/common/main.c (revision 1fa2a66491e7d8ae0be84e7da4da8e812480c710)
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 2022 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 | q_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