|
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, Version 1.0 only |
|
6 * (the "License"). You may not use this file except in compliance |
|
7 * with the License. |
|
8 * |
|
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
10 * or http://www.opensolaris.org/os/licensing. |
|
11 * See the License for the specific language governing permissions |
|
12 * and limitations under the License. |
|
13 * |
|
14 * When distributing Covered Code, include this CDDL HEADER in each |
|
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
16 * If applicable, add the following below this CDDL HEADER, with the |
|
17 * fields enclosed by brackets "[]" replaced with your own identifying |
|
18 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
19 * |
|
20 * CDDL HEADER END |
|
21 */ |
|
22 /* |
|
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. |
|
24 * Use is subject to license terms. |
|
25 */ |
|
26 |
|
27 #pragma ident "%Z%%M% %I% %E% SMI" |
|
28 |
|
29 /* Copyright (c) 1988 AT&T */ |
|
30 /* All Rights Reserved */ |
|
31 |
|
32 |
|
33 /* |
|
34 * ttyname(f): return "/dev/X" (where X is a relative pathname |
|
35 * under /dev/), which is the name of the tty character special |
|
36 * file associated with the file descriptor f, or NULL if the |
|
37 * pathname cannot be found. |
|
38 * |
|
39 * Ttyname tries to find the tty file by matching major/minor |
|
40 * device, file system ID, and inode numbers of the file |
|
41 * descriptor parameter to those of files in the /dev/ directory. |
|
42 * |
|
43 * It attempts to find a match on major/minor device numbers, |
|
44 * file system ID, and inode numbers, but failing to match on |
|
45 * all three, settles for just a match on device numbers and |
|
46 * file system ID. |
|
47 * |
|
48 * To achieve higher performance and more flexible functionality, |
|
49 * ttyname first looks for the tty file in the directories specified |
|
50 * in the configuration file /etc/ttysrch. Entries in /etc/ttysrch |
|
51 * may be qualified to specify that a partial match may be acceptable. |
|
52 * This further improves performance by allowing an entry which |
|
53 * matches major/minor and file system ID, but not inode number |
|
54 * without searching the entire /dev tree. If /etc/ttysrch does not |
|
55 * exist, ttyname looks in a default list of directories. If after |
|
56 * looking in the most likely places, ttyname still cannot find the |
|
57 * tty file, it recursively searches thru the rest of the /dev/ |
|
58 * directory. |
|
59 * |
|
60 * In addition to the public interfaces, ttyname() & ttyname_r(), which |
|
61 * do the lookup based on an open file descriptor, |
|
62 * the private interface _ttyname_dev() does the lookup based on a |
|
63 * major/minor device. It follows the same order of lookup rules and |
|
64 * returns similar information, but matches on just the major/minor |
|
65 * device numbers. |
|
66 */ |
|
67 |
|
68 #pragma weak ttyname = _ttyname |
|
69 #pragma weak ttyname_r = _ttyname_r |
|
70 |
|
71 #include "synonyms.h" |
|
72 #include "mtlib.h" |
|
73 #include "libc.h" |
|
74 #include "_libc_gettext.h" |
|
75 #include <sys/sysmacros.h> |
|
76 #include <sys/types.h> |
|
77 #include <dirent.h> |
|
78 #include <fcntl.h> |
|
79 #include <sys/stat.h> |
|
80 #include <stdlib.h> |
|
81 #include <ctype.h> |
|
82 #include <string.h> |
|
83 #include <stdio.h> |
|
84 #include <unistd.h> |
|
85 #include <thread.h> |
|
86 #include <synch.h> |
|
87 #include <errno.h> |
|
88 #include <limits.h> |
|
89 #include <sys/mkdev.h> |
|
90 #include "tsd.h" |
|
91 |
|
92 typedef struct entry { |
|
93 char *name; |
|
94 int flags; |
|
95 } entry_t; |
|
96 |
|
97 typedef struct special { |
|
98 const char *spcl_name; /* device name */ |
|
99 dev_t spcl_rdev; /* matching major/minor devnum */ |
|
100 dev_t spcl_fsdev; /* devnum of containing FS */ |
|
101 ino_t spcl_inum; /* inum of entry in FS */ |
|
102 } spcl_t; |
|
103 |
|
104 static int srch_dir(const entry_t path, int match_mask, int depth, |
|
105 const entry_t skip_dirs[], struct stat64 *fsb); |
|
106 static const entry_t *get_pri_dirs(void); |
|
107 static char *ispts(struct stat64 *fsb, int match_mask); |
|
108 static char *ispty(struct stat64 *fsb, int match_mask); |
|
109 static void itoa(int i, char *ptr); |
|
110 static char *_ttyname_common(struct stat64 *fsp, char *buffer, |
|
111 uint_t match_mask); |
|
112 |
|
113 |
|
114 #define MAX_DEV_PATH TTYNAME_MAX |
|
115 #define MAX_SRCH_DEPTH 4 |
|
116 |
|
117 #define MATCH_MM 1 |
|
118 #define MATCH_FS 2 |
|
119 #define MATCH_INO 4 |
|
120 #define MATCH_ALL 7 |
|
121 |
|
122 #define DEV "/dev" |
|
123 #define TTYSRCH "/etc/ttysrch" |
|
124 #define PTS "/dev/pts" |
|
125 |
|
126 static const entry_t dev_dir = |
|
127 { "/dev", MATCH_ALL }; |
|
128 |
|
129 static const entry_t def_srch_dirs[] = { /* default search list */ |
|
130 { "/dev/pts", MATCH_ALL }, |
|
131 { "/dev/term", MATCH_ALL }, |
|
132 { "/dev/zcons", MATCH_ALL }, |
|
133 { NULL, 0 } |
|
134 }; |
|
135 |
|
136 static char *dir_buf; /* directory buffer for ttysrch body */ |
|
137 static entry_t *dir_vec; /* directory vector for ttysrch ptrs */ |
|
138 static size_t dir_size; /* ttysrch file size */ |
|
139 static timestruc_t dir_mtim; /* ttysrch file modification time */ |
|
140 static char rbuf[MAX_DEV_PATH]; /* perfect match file name */ |
|
141 static char dev_rbuf[MAX_DEV_PATH]; /* partial match file name */ |
|
142 static int dev_flag; /* if set, dev + rdev match was found */ |
|
143 static spcl_t special_case[] = { |
|
144 "/dev/tty", 0, 0, 0, |
|
145 "/dev/console", 0, 0, 0, |
|
146 "/dev/conslog", 0, 0, 0, |
|
147 "/dev/syscon", 0, 0, 0, |
|
148 "/dev/systty", 0, 0, 0, |
|
149 "/dev/wscons", 0, 0, 0 |
|
150 }; |
|
151 #define NUMSPECIAL (sizeof (special_case) / sizeof (spcl_t)) |
|
152 static spcl_t ptmspecial = { |
|
153 "/dev/ptmx", 0, 0, 0 |
|
154 }; |
|
155 static dev_t ptcdev = NODEV; |
|
156 static dev_t ptsldev = NODEV; |
|
157 |
|
158 char * |
|
159 _ttyname_dev(dev_t rdev, char *buffer, size_t buflen) |
|
160 { |
|
161 struct stat64 fsb; |
|
162 |
|
163 fsb.st_rdev = rdev; |
|
164 |
|
165 if (buflen < MAX_DEV_PATH) { |
|
166 errno = ERANGE; |
|
167 return (NULL); |
|
168 } |
|
169 |
|
170 return (_ttyname_common(&fsb, buffer, MATCH_MM)); |
|
171 } |
|
172 |
|
173 /* |
|
174 * POSIX.1c Draft-6 version of the function ttyname_r. |
|
175 * It was implemented by Solaris 2.3. |
|
176 */ |
|
177 char * |
|
178 _ttyname_r(int f, char *buffer, int buflen) |
|
179 { |
|
180 struct stat64 fsb; /* what we are searching for */ |
|
181 /* |
|
182 * do we need to search anything at all? (is fildes a char special tty |
|
183 * file?) |
|
184 */ |
|
185 if (fstat64(f, &fsb) < 0) { |
|
186 errno = EBADF; |
|
187 return (0); |
|
188 } |
|
189 if ((isatty(f) == 0) || |
|
190 ((fsb.st_mode & S_IFMT) != S_IFCHR)) { |
|
191 errno = ENOTTY; |
|
192 return (0); |
|
193 } |
|
194 |
|
195 if (buflen < MAX_DEV_PATH) { |
|
196 errno = ERANGE; |
|
197 return (0); |
|
198 } |
|
199 |
|
200 return (_ttyname_common(&fsb, buffer, MATCH_ALL)); |
|
201 } |
|
202 |
|
203 static char * |
|
204 _ttyname_common(struct stat64 *fsp, char *buffer, uint_t match_mask) |
|
205 { |
|
206 struct stat64 tfsb; |
|
207 const entry_t *srch_dirs; /* priority directories */ |
|
208 spcl_t *spclp; |
|
209 int i; |
|
210 int found = 0; |
|
211 int dirno = 0; |
|
212 int is_pts = 0; |
|
213 char *retval = NULL; |
|
214 char *pt = NULL; |
|
215 |
|
216 /* |
|
217 * We can't use lmutex_lock() here because we call malloc()/free() |
|
218 * and _libc_gettext(). Use the brute-force fork_lock_enter(). |
|
219 */ |
|
220 (void) fork_lock_enter(NULL); |
|
221 |
|
222 /* |
|
223 * match special cases |
|
224 */ |
|
225 |
|
226 for (spclp = special_case, i = 0; i < NUMSPECIAL; spclp++, i++) { |
|
227 if ((spclp->spcl_inum | spclp->spcl_fsdev | |
|
228 spclp->spcl_rdev) == 0) { |
|
229 if (stat64(spclp->spcl_name, &tfsb) != 0) |
|
230 continue; |
|
231 spclp->spcl_rdev = tfsb.st_rdev; |
|
232 spclp->spcl_fsdev = tfsb.st_dev; |
|
233 spclp->spcl_inum = tfsb.st_ino; |
|
234 } |
|
235 if (match_mask == MATCH_MM) { |
|
236 if (spclp->spcl_rdev == fsp->st_rdev) { |
|
237 retval = strcpy(rbuf, spclp->spcl_name); |
|
238 goto out; |
|
239 } |
|
240 } else if (spclp->spcl_fsdev == fsp->st_dev && |
|
241 spclp->spcl_rdev == fsp->st_rdev && |
|
242 spclp->spcl_inum == fsp->st_ino) { |
|
243 retval = strcpy(rbuf, spclp->spcl_name); |
|
244 goto out; |
|
245 } |
|
246 } |
|
247 /* |
|
248 * additional special case: ptm clone device |
|
249 * ptm devs have no entries in /dev |
|
250 * if major number matches, just short circuit any further lookup |
|
251 * NOTE: the minor number of /dev/ptmx is the ptm major number |
|
252 */ |
|
253 spclp = &ptmspecial; |
|
254 if ((spclp->spcl_inum | spclp->spcl_fsdev | spclp->spcl_rdev) == 0) { |
|
255 if (stat64(spclp->spcl_name, &tfsb) == 0) { |
|
256 spclp->spcl_rdev = tfsb.st_rdev; |
|
257 spclp->spcl_fsdev = tfsb.st_dev; |
|
258 spclp->spcl_inum = tfsb.st_ino; |
|
259 } |
|
260 } |
|
261 if ((spclp->spcl_rdev != 0) && |
|
262 (minor(spclp->spcl_rdev) == major(fsp->st_rdev))) |
|
263 goto out; |
|
264 |
|
265 /* |
|
266 * additional special case: pty dev |
|
267 * one of the known default pairs of /dev/ptyXX or /dev/ttyXX |
|
268 */ |
|
269 if ((retval = ispty(fsp, match_mask)) != NULL) |
|
270 goto out; |
|
271 |
|
272 /* |
|
273 * search the priority directories |
|
274 */ |
|
275 |
|
276 |
|
277 srch_dirs = get_pri_dirs(); |
|
278 dev_flag = 0; |
|
279 |
|
280 while ((!found) && (srch_dirs[dirno].name != NULL)) { |
|
281 |
|
282 /* |
|
283 * if /dev is one of the priority directories, only |
|
284 * search its top level(set depth = MAX_SEARCH_DEPTH) |
|
285 */ |
|
286 |
|
287 /* |
|
288 * Is /dev/pts then just do a quick check. We don't have |
|
289 * to stat the entire /dev/pts dir. |
|
290 */ |
|
291 if (strcmp(PTS, srch_dirs[dirno].name) == NULL) { |
|
292 if ((pt = ispts(fsp, match_mask)) != NULL) { |
|
293 is_pts = 1; |
|
294 found = 1; |
|
295 } |
|
296 } else { |
|
297 found = srch_dir(srch_dirs[dirno], match_mask, |
|
298 ((strcmp(srch_dirs[dirno].name, |
|
299 dev_dir.name) == 0) ? |
|
300 MAX_SRCH_DEPTH : 1), 0, fsp); |
|
301 } |
|
302 dirno++; |
|
303 } |
|
304 |
|
305 /* |
|
306 * search the /dev/ directory, skipping priority directories |
|
307 */ |
|
308 if (!found) |
|
309 found = srch_dir(dev_dir, match_mask, 0, srch_dirs, fsp); |
|
310 |
|
311 |
|
312 /* |
|
313 * return |
|
314 */ |
|
315 |
|
316 if (found) { |
|
317 if (is_pts) |
|
318 retval = pt; |
|
319 else |
|
320 retval = rbuf; |
|
321 } else if (dev_flag) |
|
322 retval = dev_rbuf; |
|
323 else |
|
324 retval = NULL; |
|
325 out: retval = (retval ? strcpy(buffer, retval) : NULL); |
|
326 fork_lock_exit(); |
|
327 return (retval); |
|
328 } |
|
329 |
|
330 /* |
|
331 * POSIX.1c standard version of the function ttyname_r. |
|
332 * User gets it via static ttyname_r from the header file. |
|
333 */ |
|
334 int |
|
335 __posix_ttyname_r(int fildes, char *name, size_t namesize) |
|
336 { |
|
337 int nerrno = 0; |
|
338 int oerrno = errno; |
|
339 int namelen; |
|
340 |
|
341 errno = 0; |
|
342 |
|
343 if (namesize > INT_MAX) |
|
344 namelen = INT_MAX; |
|
345 else |
|
346 namelen = (int)namesize; |
|
347 |
|
348 if (_ttyname_r(fildes, name, namelen) == NULL) { |
|
349 if (errno == 0) |
|
350 nerrno = EINVAL; |
|
351 else |
|
352 nerrno = errno; |
|
353 } |
|
354 errno = oerrno; |
|
355 return (nerrno); |
|
356 } |
|
357 |
|
358 /* |
|
359 * Checks if the name is under /dev/pts directory |
|
360 */ |
|
361 static char * |
|
362 ispts(struct stat64 *fsb, int match_mask) |
|
363 { |
|
364 static char buf[MAX_DEV_PATH]; |
|
365 struct stat64 stb; |
|
366 |
|
367 (void) strcpy(buf, "/dev/pts/"); |
|
368 itoa(minor(fsb->st_rdev), buf+strlen(buf)); |
|
369 |
|
370 if (stat64(buf, &stb) != 0) |
|
371 return (NULL); |
|
372 |
|
373 if (match_mask == MATCH_MM) { |
|
374 if (stb.st_rdev == fsb->st_rdev) |
|
375 return (buf); |
|
376 } else if (stb.st_rdev == fsb->st_rdev && |
|
377 stb.st_dev == fsb->st_dev && |
|
378 stb.st_ino == fsb->st_ino) |
|
379 return (buf); |
|
380 |
|
381 return (NULL); |
|
382 } |
|
383 |
|
384 /* |
|
385 * Checks if the dev is a known pty master or slave device |
|
386 */ |
|
387 #define MAXDEFAULTPTY 48 |
|
388 |
|
389 static char * |
|
390 ispty(struct stat64 *fsb, int match_mask) |
|
391 { |
|
392 static char buf[16]; /* big enough for "/dev/XtyXX" */ |
|
393 struct stat64 stb; |
|
394 minor_t dmin; |
|
395 char prefix; |
|
396 |
|
397 if (ptsldev == NODEV && stat64("/dev/ttyp0", &stb) == 0) |
|
398 ptsldev = stb.st_rdev; |
|
399 |
|
400 /* |
|
401 * check for a ptsl dev (/dev/ttyXX) |
|
402 */ |
|
403 prefix = 't'; |
|
404 if (major(fsb->st_rdev) != major(ptsldev)) { |
|
405 /* |
|
406 * not a ptsl, check for a ptc |
|
407 */ |
|
408 if (ptcdev == NODEV && stat64("/dev/ptyp0", &stb) == 0) |
|
409 ptcdev = stb.st_rdev; |
|
410 |
|
411 /* |
|
412 * check for a ptc dev (/dev/ptyXX) |
|
413 */ |
|
414 prefix = 'p'; |
|
415 if (major(fsb->st_rdev) != major(ptcdev)) |
|
416 return (NULL); |
|
417 } |
|
418 |
|
419 /* |
|
420 * check if minor number is in the known range |
|
421 */ |
|
422 dmin = minor(fsb->st_rdev); |
|
423 if (dmin > MAXDEFAULTPTY) |
|
424 return (NULL); |
|
425 |
|
426 /* |
|
427 * modify name based on minor number |
|
428 */ |
|
429 (void) snprintf(buf, sizeof (buf), "/dev/%cty%c%c", |
|
430 prefix, 'p' + dmin / 16, "0123456789abcdef"[dmin % 16]); |
|
431 |
|
432 if (stat64(buf, &stb) != 0) |
|
433 return (NULL); |
|
434 |
|
435 if (match_mask == MATCH_MM) { |
|
436 if (stb.st_rdev == fsb->st_rdev) |
|
437 return (buf); |
|
438 } else if (stb.st_rdev == fsb->st_rdev && |
|
439 stb.st_dev == fsb->st_dev && |
|
440 stb.st_ino == fsb->st_ino) |
|
441 return (buf); |
|
442 |
|
443 return (NULL); |
|
444 } |
|
445 |
|
446 |
|
447 /* |
|
448 * Converts a number to a string (null terminated). |
|
449 */ |
|
450 static void |
|
451 itoa(int i, char *ptr) |
|
452 { |
|
453 int dig = 0; |
|
454 int tempi; |
|
455 |
|
456 tempi = i; |
|
457 do { |
|
458 dig++; |
|
459 tempi /= 10; |
|
460 } while (tempi); |
|
461 |
|
462 ptr += dig; |
|
463 *ptr = '\0'; |
|
464 while (--dig >= 0) { |
|
465 *(--ptr) = i % 10 + '0'; |
|
466 i /= 10; |
|
467 } |
|
468 } |
|
469 |
|
470 /* |
|
471 * srch_dir() searches directory path and all directories under it up |
|
472 * to depth directories deep for a file described by a stat structure |
|
473 * fsb. It puts the answer into rbuf. If a match is found on device |
|
474 * number only, the file name is put into dev_rbuf and dev_flag is set. |
|
475 * |
|
476 * srch_dir() returns 1 if a match (on device and inode) is found, |
|
477 * or 0 otherwise. |
|
478 * |
|
479 */ |
|
480 |
|
481 static int |
|
482 srch_dir(const entry_t path, /* current path */ |
|
483 int match_mask, /* flags mask */ |
|
484 int depth, /* current depth (/dev = 0) */ |
|
485 const entry_t skip_dirs[], /* directories not needing searching */ |
|
486 struct stat64 *fsb) /* the file being searched for */ |
|
487 { |
|
488 DIR *dirp; |
|
489 struct dirent64 *direntp; |
|
490 struct stat64 tsb; |
|
491 char file_name[MAX_DEV_PATH]; |
|
492 entry_t file; |
|
493 char *last_comp; |
|
494 int found = 0; |
|
495 int dirno = 0; |
|
496 size_t path_len; |
|
497 |
|
498 file.name = file_name; |
|
499 file.flags = path.flags & match_mask; |
|
500 if (file.flags == 0) |
|
501 file.flags = match_mask; |
|
502 |
|
503 /* |
|
504 * do we need to search this directory? (always search /dev at depth 0) |
|
505 */ |
|
506 if ((skip_dirs != NULL) && (depth != 0)) |
|
507 while (skip_dirs[dirno].name != NULL) |
|
508 if (strcmp(skip_dirs[dirno++].name, path.name) == 0) |
|
509 return (0); |
|
510 |
|
511 /* |
|
512 * open directory |
|
513 */ |
|
514 if ((dirp = opendir(path.name)) == NULL) { |
|
515 return (0); |
|
516 } |
|
517 |
|
518 /* |
|
519 * skip two first entries ('.' and '..') |
|
520 */ |
|
521 if (((direntp = readdir64(dirp)) == NULL) || |
|
522 ((direntp = readdir64(dirp)) == NULL)) { |
|
523 (void) closedir(dirp); |
|
524 return (0); |
|
525 } |
|
526 |
|
527 path_len = strlen(path.name); |
|
528 (void) strcpy(file_name, path.name); |
|
529 last_comp = file_name + path_len; |
|
530 *last_comp++ = '/'; |
|
531 |
|
532 /* |
|
533 * read thru the directory |
|
534 */ |
|
535 while ((!found) && ((direntp = readdir64(dirp)) != NULL)) { |
|
536 |
|
537 /* |
|
538 * if the file name (path + "/" + d_name + NULL) would be too |
|
539 * long, skip it |
|
540 */ |
|
541 if ((path_len + strlen(direntp->d_name) + 2) > MAX_DEV_PATH) |
|
542 continue; |
|
543 else |
|
544 (void) strcpy(last_comp, direntp->d_name); |
|
545 |
|
546 if (stat64(file_name, &tsb) < 0) |
|
547 continue; |
|
548 |
|
549 /* |
|
550 * if a file is a directory and we are not too deep, recurse |
|
551 */ |
|
552 if ((tsb.st_mode & S_IFMT) == S_IFDIR) |
|
553 if (depth < MAX_SRCH_DEPTH) |
|
554 found = srch_dir(file, match_mask, depth+1, |
|
555 skip_dirs, fsb); |
|
556 else |
|
557 continue; |
|
558 |
|
559 /* |
|
560 * else if it is not a directory, is it a character special |
|
561 * file? |
|
562 */ |
|
563 else if ((tsb.st_mode & S_IFMT) == S_IFCHR) { |
|
564 int flag = 0; |
|
565 if (tsb.st_dev == fsb->st_dev) |
|
566 flag |= MATCH_FS; |
|
567 if (tsb.st_rdev == fsb->st_rdev) |
|
568 flag |= MATCH_MM; |
|
569 if (tsb.st_ino == fsb->st_ino) |
|
570 flag |= MATCH_INO; |
|
571 |
|
572 if ((flag & file.flags) == file.flags) { |
|
573 (void) strcpy(rbuf, file.name); |
|
574 found = 1; |
|
575 } else if ((flag & (MATCH_MM | MATCH_FS)) == |
|
576 (MATCH_MM | MATCH_FS)) { |
|
577 |
|
578 /* |
|
579 * no (inodes do not match), but save the name |
|
580 * for later |
|
581 */ |
|
582 (void) strcpy(dev_rbuf, file.name); |
|
583 dev_flag = 1; |
|
584 } |
|
585 } |
|
586 } |
|
587 (void) closedir(dirp); |
|
588 return (found); |
|
589 } |
|
590 |
|
591 |
|
592 /* |
|
593 * get_pri_dirs() - returns a pointer to an array of strings, where each string |
|
594 * is a priority directory name. The end of the array is marked by a NULL |
|
595 * pointer. The priority directories' names are obtained from the file |
|
596 * /etc/ttysrch if it exists and is readable, or if not, a default hard-coded |
|
597 * list of directories. |
|
598 * |
|
599 * /etc/ttysrch, if used, is read in as a string of characters into memory and |
|
600 * then parsed into strings of priority directory names, omitting comments and |
|
601 * blank lines. |
|
602 * |
|
603 */ |
|
604 |
|
605 #define START_STATE 1 |
|
606 #define COMMENT_STATE 2 |
|
607 #define DIRNAME_STATE 3 |
|
608 #define FLAG_STATE 4 |
|
609 #define CHECK_STATE 5 |
|
610 |
|
611 #define COMMENT_CHAR '#' |
|
612 #define EOLN_CHAR '\n' |
|
613 |
|
614 static const entry_t * |
|
615 get_pri_dirs(void) |
|
616 { |
|
617 int fd, state; |
|
618 size_t sz; |
|
619 ssize_t size; |
|
620 struct stat64 sb; |
|
621 char *buf, *ebuf; |
|
622 entry_t *vec; |
|
623 |
|
624 /* |
|
625 * if no /etc/ttysrch, use defaults |
|
626 */ |
|
627 if ((fd = open(TTYSRCH, 0)) < 0) |
|
628 return (def_srch_dirs); |
|
629 |
|
630 if (fstat64(fd, &sb) < 0) { |
|
631 (void) close(fd); |
|
632 return (def_srch_dirs); |
|
633 } |
|
634 |
|
635 sz = (size_t)sb.st_size; |
|
636 if (dir_vec != NULL && sz == dir_size && |
|
637 sb.st_mtim.tv_sec == dir_mtim.tv_sec && |
|
638 sb.st_mtim.tv_nsec == dir_mtim.tv_nsec) { |
|
639 /* |
|
640 * size & modification time match |
|
641 * no need to reread TTYSRCH |
|
642 * just return old pointer |
|
643 */ |
|
644 (void) close(fd); |
|
645 return (dir_vec); |
|
646 } |
|
647 buf = realloc(dir_buf, sz + 1); |
|
648 if (buf != NULL) { |
|
649 dir_buf = buf; |
|
650 size = read(fd, dir_buf, sz); |
|
651 } |
|
652 (void) close(fd); |
|
653 |
|
654 if (buf == NULL || size < 0) { |
|
655 if (dir_vec != NULL) { |
|
656 free(dir_vec); |
|
657 dir_vec = NULL; |
|
658 } |
|
659 return ((entry_t *)def_srch_dirs); |
|
660 } |
|
661 dir_size = sz; |
|
662 dir_mtim = sb.st_mtim; |
|
663 |
|
664 /* |
|
665 * ensure newline termination for buffer. Add an extra |
|
666 * entry to dir_vec for null terminator |
|
667 */ |
|
668 ebuf = &dir_buf[size]; |
|
669 *ebuf++ = '\n'; |
|
670 for (sz = 1, buf = dir_buf; buf < ebuf; ++buf) |
|
671 if (*buf == '\n') |
|
672 ++sz; |
|
673 |
|
674 sz *= sizeof (*dir_vec); |
|
675 vec = realloc(dir_vec, sz); |
|
676 if (vec == NULL) { |
|
677 if (dir_vec != NULL) { |
|
678 free(dir_vec); |
|
679 dir_vec = NULL; |
|
680 } |
|
681 return (def_srch_dirs); |
|
682 } |
|
683 dir_vec = vec; |
|
684 state = START_STATE; |
|
685 for (buf = dir_buf; buf < ebuf; ++buf) { |
|
686 switch (state) { |
|
687 |
|
688 case START_STATE: |
|
689 if (*buf == COMMENT_CHAR) { |
|
690 state = COMMENT_STATE; |
|
691 break; |
|
692 } |
|
693 if (!isspace(*buf)) /* skip leading white space */ |
|
694 state = DIRNAME_STATE; |
|
695 vec->name = buf; |
|
696 vec->flags = 0; |
|
697 break; |
|
698 |
|
699 case COMMENT_STATE: |
|
700 if (*buf == EOLN_CHAR) |
|
701 state = START_STATE; |
|
702 break; |
|
703 |
|
704 case DIRNAME_STATE: |
|
705 if (*buf == EOLN_CHAR) { |
|
706 state = CHECK_STATE; |
|
707 *buf = '\0'; |
|
708 } else if (isspace(*buf)) { |
|
709 /* skip trailing white space */ |
|
710 state = FLAG_STATE; |
|
711 *buf = '\0'; |
|
712 } |
|
713 break; |
|
714 |
|
715 case FLAG_STATE: |
|
716 switch (*buf) { |
|
717 case 'M': |
|
718 vec->flags |= MATCH_MM; |
|
719 break; |
|
720 case 'F': |
|
721 vec->flags |= MATCH_FS; |
|
722 break; |
|
723 case 'I': |
|
724 vec->flags |= MATCH_INO; |
|
725 break; |
|
726 case EOLN_CHAR: |
|
727 state = CHECK_STATE; |
|
728 break; |
|
729 } |
|
730 break; |
|
731 |
|
732 case CHECK_STATE: |
|
733 if (strncmp(vec->name, DEV, strlen(DEV)) != 0) { |
|
734 int tfd = open("/dev/console", O_WRONLY); |
|
735 if (tfd >= 0) { |
|
736 char buf[256]; |
|
737 /* LINTED variable format specifier */ |
|
738 (void) snprintf(buf, sizeof (buf), |
|
739 _libc_gettext( |
|
740 "ERROR: Entry '%s' in /etc/ttysrch ignored.\n"), |
|
741 vec->name); |
|
742 (void) write(tfd, buf, strlen(buf)); |
|
743 (void) close(tfd); |
|
744 } |
|
745 } else { |
|
746 char *slash; |
|
747 slash = vec->name + strlen(vec->name) - 1; |
|
748 while (*slash == '/') |
|
749 *slash-- = '\0'; |
|
750 if (vec->flags == 0) |
|
751 vec->flags = MATCH_ALL; |
|
752 vec++; |
|
753 } |
|
754 state = START_STATE; |
|
755 /* |
|
756 * This state does not consume a character, so |
|
757 * reposition the pointer. |
|
758 */ |
|
759 buf--; |
|
760 break; |
|
761 |
|
762 } |
|
763 } |
|
764 vec->name = NULL; |
|
765 return (dir_vec); |
|
766 } |
|
767 |
|
768 |
|
769 char * |
|
770 ttyname(int f) |
|
771 { |
|
772 char *ans = tsdalloc(_T_TTYNAME, MAX_DEV_PATH, NULL); |
|
773 |
|
774 if (ans == NULL) |
|
775 return (NULL); |
|
776 return (_ttyname_r(f, ans, MAX_DEV_PATH)); |
|
777 } |