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) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
23
24
25 /*
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29
30 /*
31 * Copyright (c) 2018, Joyent, Inc.
32 */
33
34 /*
35 * setuname [-t] [-s name] [-n node]
36 */
37
38 /*
39 * Header files referenced:
40 * <stdio.h> Standard I/O
41 * <unistd.h> Standard UNIX definitions
42 * <string.h> String handling
43 * <fmtmsg.h> Standard message generation
44 * <ctype.h> Character types
45 * <errno.h> Error handling
46 * <signal.h> Signal handling
47 * <sys/types.h> Data types
48 * <sys/fcntl.h> File control
49 * <sys/utsname.h> System Name
50 * <sys/sys3b.h> sys3b() definitions
51 * <nlist.h> Definitions for Sun symbol table entries
52 */
53
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <string.h>
57 #include <fmtmsg.h>
58 #include <ctype.h>
59 #include <errno.h>
60 #include <signal.h>
61 #include <sys/types.h>
62 #include <sys/uio.h>
63 #include <sys/fcntl.h>
64 #include <sys/psw.h>
65 #include <sys/utsname.h>
66
67 #if u3b || u3b15 || u3b2
68 #include <sys/sys3b.h>
69 #endif
70
71 #if sun
72 #include <nlist.h>
73 #include <kvm.h>
74 #endif
75
76 /*
77 * Externals referenced (and not defined in a header)
78 * optind index to the next arg for getopt()
79 * opterr FLAG, TRUE tells getopt() to write messages
80 * optarg Ptr to an option's argument
81 * getopt() Gets an option from the command line
82 * putenv() Writes values into the environment
83 * exit() Exit the process
84 * access() Check accessibility of a file
85 * malloc() Allocate a block of main memory
86 * free() Free allocated space
87 * lseek() Seek within a file
88 * open() Open a file
89 * close() Close an open file
90 */
91
92 extern int optind; /* argv[] index of next arg */
93 extern int opterr; /* TRUE if getopt() is to print msgs */
94 extern char *optarg; /* Argument to parsed option */
95 extern int getopt(); /* Get an option from the command line */
96 extern int putenv(); /* Put a value into the environment */
97 extern void exit(); /* Exit the process */
98 extern int access(); /* Check the accessibility of a file */
99 extern void *malloc(); /* Get a chunk of main memory */
100 extern void free(); /* Free alloc'd space */
101 extern long lseek(); /* Seek within a file */
102 extern int open(); /* Open a file */
103 extern int close(); /* Close an open a file */
104
105 /*
106 * L O C A L D E F I N I T I O N S
107 */
108
109 /*
110 * Constants
111 */
112
113 #ifndef TRUE
114 #define TRUE (1)
115 #endif
116
117 #ifndef FALSE
118 #define FALSE (0)
119 #endif
120
121 #ifndef NULL
122 #define NULL (0)
123 #endif
124
125 #define OPTSTRING "tn:s:"
126
127 #define EX_OK 0
128 #define EX_ERROR 1
129
130 #define RC_FILENAME "/etc/rc2.d/S18setuname"
131 #define RC_DIRNAME "/etc/rc2.d"
132
133
134 /*
135 * Messages
136 */
137
138 #define E_USAGE "usage: setuname [-t] [-s name] [-n node]"
139 #define E_MISSING "Either -s name or -n node must be specified"
140 #define E_UNAME "Unable to get existing uname values"
141 #define E_INVNAME "System-name invalid: %s"
142 #define E_LONGNAME "System-name too long: %s"
143 #define E_INVNODE "Network node-name invalid: %s"
144 #define E_LONGNODE "Network node-name too long: %s"
145 #define E_NOPERMS "No permissions, request denied"
146 #define E_NOSUCHDIR "Directory doesn't exist: %s"
147 #define E_INTERNAL "Internal error: %d"
148
149 /*
150 * Macros:
151 * stdmsg(r,l,s,t) Write a standard message.
152 * 'r' is the recoverability flag
153 * 'l' is the label
154 * 's' is the severity
155 * 't' is the text.
156 * strend(p) Return the address of the end of a string
157 * (This is supposed to be defined in <sys/inline.h>
158 * but that file has string-handing def'ns that
159 * conflict with <string.h>, so we can't use it!
160 * MR dn89-04701 requests this fix.
161 */
162
163 #define stdmsg(r,l,s,t) (void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,t,MM_NULLACT,MM_NULLTAG)
164 #define strend(p) strrchr(p,'\0')
165
166 /*
167 * Local functions:
168 * setuname Changes the system name and the network node name
169 */
170
171 static int setuname(); /* This does the "real" work */
172
173
174 /*
175 * Local data
176 * lbl Buffer for the standard message label
177 * txt Buffer for the standard message text
178 */
179
180 static char lbl[MM_MXLABELLN+1]; /* Space for std msg label */
181 static char msg[MM_MXTXTLN+1]; /* Space for std msg text */
182
183 /*
184 * int main(argc, argv)
185 * int argc
186 * char *argv;
187 */
188
189 int
main(argc,argv)190 main(argc, argv)
191 int argc; /* Argument count */
192 char *argv[]; /* Argument vector */
193 {
194 /* Automatic data */
195 char *n_arg; /* Ptr to arg for -n */
196 char *s_arg; /* Ptr to arg for -s */
197 int t_seen; /* FLAG, -t option seen */
198 char *cmdname; /* Ptr to the command's name */
199 char *p; /* Temp pointer */
200 int usageerr; /* FLAG, TRUE if usage error */
201 int exitcode; /* Value to exit with */
202 int c; /* Temp character */
203 int ok; /* Flag, everything okay? */
204
205 /* Build the standard-message label */
206 if (p = strrchr(argv[0], '/')) cmdname = p+1;
207 else cmdname = argv[0];
208 (void) strcat(strcpy(lbl, "UX:"), cmdname);
209
210 /* Make only the text in standard messages appear (SVR4.0 only) */
211 (void) putenv("MSGVERB=text");
212
213
214 /* Initializations */
215 n_arg = s_arg = (char *) NULL;
216 t_seen = FALSE;
217
218
219 /*
220 * Parse command
221 */
222
223 usageerr = FALSE;
224 opterr = FALSE;
225 while (!usageerr && (c = getopt(argc, argv, OPTSTRING)) != EOF) switch(c) {
226
227 case 'n': /* -n node */
228 if (n_arg) usageerr = TRUE;
229 else n_arg = optarg;
230 break;
231
232 case 's': /* -s name */
233 if (s_arg) usageerr = TRUE;
234 else s_arg = optarg;
235 break;
236
237 case 't': /* -t */
238 if (t_seen) usageerr = TRUE;
239 else t_seen = TRUE;
240 break;
241
242 default: /* Something that doesn't exist */
243 usageerr = TRUE;
244 } /* switch() */
245
246 /* If there was a usage error, report the error and exit */
247 if ((argc >= (optind+1)) || usageerr) {
248 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE);
249 exit(EX_ERROR);
250 }
251
252 /* Either -n <node> or -s <name> has to be specified */
253 if (!(n_arg || s_arg)) {
254 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MISSING);
255 exit(EX_ERROR);
256 }
257
258
259 /*
260 * Validate arguments:
261 * - The length of the system name must be less than SYS_NMLN-1
262 * characters,
263 * - The length of the network node-name must be less than
264 * SYS_NMLN-1 characters,
265 * - The system name must equal [a-zA-Z0-9-_]+,
266 * - The network node-name must equal [a-zA-Z0-9-_]+.
267 */
268
269 /* Check the length and the character-set of the system name */
270 if (s_arg) {
271
272 /* Check length of the system name */
273 if (strlen(s_arg) > (size_t)(SYS_NMLN-1)) {
274 (void) sprintf(msg, E_LONGNAME, s_arg);
275 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
276 exit(EX_ERROR);
277 }
278
279 /* Check the character-set */
280 ok = TRUE;
281 for (p = s_arg ; ok && *p ; p++) {
282 if (!isalnum(*p) && (*p != '-') && (*p != '_')) ok = FALSE;
283 }
284 if (!ok || (p == s_arg)) {
285 (void) sprintf(msg, E_INVNAME, s_arg);
286 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
287 exit(EX_ERROR);
288 }
289 }
290
291 /* Check the length and the character-set of the network node-name */
292
293 if (n_arg) {
294
295 /* Check length of the network node-name */
296 if (strlen(n_arg) > (size_t)(SYS_NMLN-1)) {
297 (void) sprintf(msg, E_LONGNODE, n_arg);
298 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
299 exit(EX_ERROR);
300 }
301
302 /* Check the character-set */
303 ok = TRUE;
304 for (p = n_arg ; ok && *p ; p++) {
305 if (!isalnum(*p) && (*p != '-') && (*p != '_')) ok = FALSE;
306 }
307 if (!ok || (p == n_arg)) {
308 (void) sprintf(msg, E_INVNODE, n_arg);
309 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
310 exit(EX_ERROR);
311 }
312 }
313
314
315 /*
316 * Make sure we have access to needed resources:
317 * - Read/write access to kernel memory (/dev/kmem)
318 * - If -t is not specified, read/write access to /etc/rc2.d
319 * - If -t is not specified, read access to /etc/rc2.d/S18setuname
320 */
321
322 if (access("/dev/kmem", R_OK|W_OK) == 0) {
323 if (access(RC_DIRNAME, R_OK|W_OK) == 0) {
324 if ((access(RC_FILENAME, R_OK) != 0) &&
325 (access(RC_FILENAME, F_OK) == 0)) {
326 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOPERMS);
327 exit(EX_ERROR);
328 }
329 }
330 else {
331 if (access(RC_DIRNAME, F_OK) == 0) {
332 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOPERMS);
333 exit(EX_ERROR);
334 }
335 else {
336 (void) sprintf(msg, E_NOSUCHDIR, RC_DIRNAME);
337 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
338 exit(EX_ERROR);
339 }
340 }
341 }
342 else {
343 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOPERMS);
344 exit(EX_ERROR);
345 }
346
347
348 /* Attempt the setuname */
349 if (setuname(t_seen, s_arg, n_arg) == 0) exitcode = EX_OK;
350 else {
351 (void) sprintf(msg, E_INTERNAL, errno);
352 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg);
353 exitcode = EX_ERROR;
354 }
355
356 /* Finished */
357 return (exitcode);
358 } /* main() */
359
360 /*
361 * int setuname(temp, name, node)
362 * int temp
363 * char *name
364 * char *node
365 *
366 * Set any or all of the following machine parameters, either
367 * temporarily or permanently, depending on <temp>.
368 * - System name
369 * - Network Node-name
370 */
371
372 static int
setuname(temp,sysname,nodename)373 setuname(temp, sysname, nodename)
374 int temp; /* Set in kernel only flag */
375 char *sysname; /* System name */
376 char *nodename; /* Network node-name */
377 {
378 /* Automatic Data */
379 struct utsname utsname; /* Space for the kernel's utsname information */
380 #if u3b || u3b15 || u3b2
381 struct s3bsym *symbtbl; /* The kernel's symbol table */
382 #endif
383 #if sun
384 struct nlist nl[] = {
385 {"utsname", 0, 0, 0, 0, 0},
386 {NULL}
387 };
388 kvm_t *kd;
389 #endif
390 uintptr_t utsname_addr; /* Addr of "utsname" in the kernel */
391 char *sysnm = (char *)NULL; /* System name to set (from file or arg) */
392 char *nodenm = (char *)NULL; /* Network node-name to set (from file or arg) */
393 FILE *fd; /* Std I/O File Descriptor for /etc/rc2.d/S18setuname */
394 char *p; /* Temp pointer */
395 void (*oldsighup)(); /* Function to call for SIGHUP */
396 void (*oldsigint)(); /* Function to call for SIGINT */
397 int rtncd; /* Value to return to the caller */
398 unsigned long symbtblsz; /* The size of the kernel's symbol table, in bytes */
399 int memfd; /* File descriptor: open kernel memory */
400 int i; /* Temp counter */
401
402
403 /* Nothing's gone wrong yet (but we've only just begun!) */
404 rtncd = 0;
405
406
407 /*
408 * Get the virtual address of the symbol "utsname" in the kernel
409 * so we can get set the system name and/or the network node-name
410 * directly in the kernel's memory space.
411 */
412
413 #if u3b || u3b15 || u3b2
414 if ((sys3b(S3BSYM, (struct s3bsym *) &symbtblsz, sizeof(symbtblsz)) == 0) &&
415 (symbtbl = (struct s3bsym *) malloc(symbtblsz))) {
416
417 (void) sys3b(S3BSYM, symbtbl, symbtblsz);
418 p = (char *) symbtbl;
419 for (i = symbtbl->count; i-- && (strcmp(p, "utsname") != 0) ; p = S3BNXTSYM(p)) ;
420 if (i >= 0) utsname_addr = S3BSVAL(p);
421 else rtncd = -1;
422 free((void *) symbtbl);
423
424 } else rtncd = -1;
425
426 #elif sun
427 /* Check out namelist and memory files. */
428 if ((kd = kvm_open(NULL, NULL, NULL, O_RDWR, NULL)) == NULL)
429 rtncd = -1;
430 else if (kvm_nlist(kd, nl) != 0)
431 rtncd = -1;
432 else if (nl[0].n_value == 0)
433 rtncd = -1;
434 else
435 utsname_addr = (uintptr_t)nl[0].n_value;
436 #else
437 if (nlist("/unix", nl) != 0)
438 rtncd = -1;
439 #endif
440 if (rtncd != 0) return(rtncd);
441
442 /*
443 * Open the kernel's memory, get the existing "utsname" structure,
444 * change the system name and/or the network node-name in that struct,
445 * write it back out to kernel memory, then close kernel memory.
446 */
447 #ifdef sun
448 if (kvm_kread(kd, utsname_addr, &utsname, sizeof (utsname)) ==
449 sizeof (utsname)) {
450 if (sysname)
451 (void) strncpy(utsname.sysname, sysname,
452 sizeof (utsname.sysname));
453 if (nodename)
454 (void) strncpy(utsname.nodename, nodename,
455 sizeof (utsname.nodename));
456 (void) kvm_kwrite(kd, utsname_addr, &utsname, sizeof (utsname));
457 (void) kvm_close(kd);
458 } else
459 return (-1);
460 #else /* sun */
461 if ((memfd = open("/dev/kmem", O_RDWR, 0)) > 0) {
462 if ((lseek(memfd, (long) utsname_addr, SEEK_SET) != -1) &&
463 (read(memfd, &utsname, sizeof(utsname)) == sizeof(utsname))) {
464 if (sysname) (void) strncpy(utsname.sysname, sysname, sizeof(utsname.sysname));
465 if (nodename) (void) strncpy(utsname.nodename, nodename, sizeof(utsname.nodename));
466 (void) lseek(memfd, (long) utsname_addr, SEEK_SET);
467 (void) write(memfd, &utsname, sizeof(utsname));
468 (void) close(memfd);
469 } else rtncd = -1;
470 } else rtncd = -1;
471 if (rtncd != 0) return(rtncd);
472 #endif /* sun */
473
474
475 /*
476 * If the "temp" flag is FALSE, we need to permanently set the
477 * system name in the file /etc/rc2.d/S18setuname
478 */
479
480 if (!temp) {
481 /*
482 * If a name was specified by the caller, use that, otherwise, use
483 * whatever was in the "rc" file.
484 */
485
486 if (sysname) sysnm = sysname;
487 if (nodename) nodenm = nodename;
488
489
490 /*
491 * Write the file /etc/rc2.d/S18setuname so that the system name is
492 * set on boots and state changes.
493 *
494 * DISABLED SIGNALS: SIGHUP, SIGINT
495 */
496
497 /* Give us a reasonable chance to complete without interruptions */
498 oldsighup = signal(SIGHUP, SIG_IGN);
499 oldsigint = signal(SIGINT, SIG_IGN);
500
501 /* Write the new setuname "rc" file */
502 if (sysname != NULL) {
503 if ((fd = fopen(RC_FILENAME, "w")) != (FILE *) NULL) {
504 (void) fprintf(fd, "# %s\n", sysnm);
505 (void) fprintf(fd, "#\n");
506 (void) fprintf(fd, "# This script, generated by the setuname command,\n");
507 (void) fprintf(fd, "# sets the system's system-name\n");
508 (void) fprintf(fd, "#\n");
509 if (sysnm && *sysnm)
510 (void) fprintf(fd, "setuname -t -s %s\n", sysnm);
511 (void) fclose(fd);
512 } else return(rtncd = -1);
513 }
514
515 if(nodename != NULL) {
516 char curname[SYS_NMLN];
517 int curlen;
518 FILE *file;
519
520 if ((file = fopen("/etc/nodename", "r")) != NULL) {
521 curlen = fread(curname, sizeof(char), SYS_NMLN, file);
522 for (i = 0; i < curlen; i++) {
523 if (curname[i] == '\n') {
524 curname[i] = '\0';
525 break;
526 }
527 }
528 if (i == curlen) {
529 curname[curlen] = '\0';
530 }
531 (void)fclose(file);
532 } else {
533 curname[0] = '\0';
534 }
535 if (strcmp(curname, nodenm) != 0) {
536 if ((file = fopen("/etc/nodename", "w")) == NULL) {
537 (void) fprintf(stderr, "setuname: error in writing name\n");
538 exit(1);
539 }
540 if (fprintf(file, "%s\n", nodenm) < 0) {
541 (void) fprintf(stderr, "setuname: error in writing name\n");
542 exit(1);
543 }
544 (void)fclose(file);
545 }
546 }
547 /* Restore signal handling */
548 (void) signal(SIGHUP, oldsighup);
549 (void) signal(SIGINT, oldsigint);
550 } /* if (!temp) */
551
552 /* Fini */
553 return(rtncd);
554 }
555