1 /* Various utilities - Unix variants
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
3 Written 1994, 1995, 1996 by:
4 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
5 Jakub Jelinek, Mauricio Plaza.
7 The file_date routine is mostly from GNU's fileutils package,
8 written by Richard Stallman and David MacKenzie.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
27 #include <sys/types.h>
32 #include <signal.h> /* my_system */
33 #include <limits.h> /* INT_MAX */
34 #include <sys/time.h> /* select: timeout */
35 #include <sys/param.h>
36 #include <sys/types.h>
39 #ifdef HAVE_SYS_WAIT_H
40 # include <sys/wait.h> /* my_system */
42 #include <errno.h> /* my_system */
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
51 # include <sys/timeb.h>
55 # if defined(__GLIBC__) && (__GLIBC__ < 2)
56 # include <linux/termios.h> /* This is needed for TIOCLINUX */
60 # include <sys/ioctl.h>
63 # include <unix.h> /* exec*() from <process.h> */
69 #include "mountlist.h"
71 #include "dialog.h" /* message() */
72 #include "../vfs/vfs.h" /* mc_read() */
75 struct sigaction startup_handler
;
76 extern struct mount_entry
*mount_list
;
78 uid_t current_user_uid
;
79 user_in_groups
*current_user_gid
;
90 files
= sysconf (_SC_OPEN_MAX
);
95 return files
= OPEN_MAX
;
101 void init_groups (void)
106 user_in_groups
*cug
, *pug
;
108 pwd
= getpwuid (current_user_uid
=getuid ());
110 current_user_gid
= (pug
= xmalloc (sizeof (user_in_groups
), "init_groups"));
111 current_user_gid
->gid
= getgid (); current_user_gid
->next
= 0;
117 while ((grp
= getgrent ()))
118 for (i
= 0; grp
->gr_mem
[i
]; i
++)
119 if (!strcmp (pwd
->pw_name
,grp
->gr_mem
[i
]))
121 cug
= xmalloc (sizeof (user_in_groups
), "init_groups");
122 cug
->gid
= grp
->gr_gid
;
131 /* Return the index of permission triplet */
133 get_user_rights (struct stat
*buf
)
137 if (buf
->st_uid
== current_user_uid
|| current_user_uid
== 0)
140 for (cug
= current_user_gid
; cug
; cug
= cug
->next
)
141 if (cug
->gid
== buf
->st_gid
) return 1;
150 user_in_groups
*pug
, *cug
= current_user_gid
;
159 #define UID_CACHE_SIZE 200
160 #define GID_CACHE_SIZE 30
167 int_cache uid_cache
[UID_CACHE_SIZE
];
168 int_cache gid_cache
[GID_CACHE_SIZE
];
170 void init_uid_gid_cache (void)
174 for (i
= 0; i
< UID_CACHE_SIZE
; i
++)
175 uid_cache
[i
].string
= 0;
177 for (i
= 0; i
< GID_CACHE_SIZE
; i
++)
178 gid_cache
[i
].string
= 0;
181 static char *i_cache_match (int id
, int_cache
*cache
, int size
)
185 for (i
= 0; i
< size
; i
++)
186 if (cache
[i
].index
== id
)
187 return cache
[i
].string
;
191 static void i_cache_add (int id
, int_cache
*cache
, int size
, char *text
,
194 if (cache
[*last
].string
)
195 free (cache
[*last
].string
);
196 cache
[*last
].string
= strdup (text
);
197 cache
[*last
].index
= id
;
198 *last
= ((*last
)+1) % size
;
201 char *get_owner (int uid
)
204 static char ibuf
[8];
208 if ((name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
)) != NULL
)
211 pwd
= getpwuid (uid
);
213 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
217 sprintf (ibuf
, "%d", uid
);
222 char *get_group (int gid
)
225 static char gbuf
[8];
229 if ((name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
)) != NULL
)
232 grp
= getgrgid (gid
);
234 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
237 sprintf (gbuf
, "%d", gid
);
242 /* Since ncurses uses a handler that automatically refreshes the */
243 /* screen after a SIGCONT, and we don't want this behavior when */
244 /* spawning a child, we save the original handler here */
245 void save_stop_handler (void)
247 sigaction (SIGTSTP
, NULL
, &startup_handler
);
251 #define PORT_HAS_MY_SYSTEM 1
254 #ifndef PORT_HAS_MY_SYSTEM
255 int my_system (int flags
, const char *shell
, const char *command
)
257 struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
260 int as_shell_command
= flags
& EXECUTE_AS_SHELL
;
262 ignore
.sa_handler
= SIG_IGN
;
263 sigemptyset (&ignore
.sa_mask
);
266 sigaction (SIGINT
, &ignore
, &save_intr
);
267 sigaction (SIGQUIT
, &ignore
, &save_quit
);
269 /* Restore the original SIGTSTP handler, we don't want ncurses' */
270 /* handler messing the screen after the SIGCONT */
271 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
273 if ((pid
= fork ()) < 0){
274 fprintf (stderr
, "\n\nfork () = -1\n");
278 sigaction (SIGINT
, &save_intr
, NULL
);
279 sigaction (SIGQUIT
, &save_quit
, NULL
);
282 prepare_environment ();
285 if (as_shell_command
)
286 execl (shell
, shell
, "-c", command
, (char *) 0);
288 execlp (shell
, shell
, command
, (char *) 0);
290 _exit (127); /* Exec error */
292 while (waitpid (pid
, &status
, 0) < 0)
298 sigaction (SIGINT
, &save_intr
, NULL
);
299 sigaction (SIGQUIT
, &save_quit
, NULL
);
300 sigaction (SIGTSTP
, &save_stop
, NULL
);
303 waitpid(-1, NULL
, WNOHANG
);
304 #endif /* SCO_FLAVOR */
306 return WEXITSTATUS(status
);
310 /* Returns a newly allocated string, if directory does not exist, return 0 */
311 char *tilde_expand (char *directory
)
313 struct passwd
*passwd
;
318 if (*directory
!= '~')
319 return strdup (directory
);
323 p
= strchr (directory
, PATH_SEP
);
325 /* d = "~" or d = "~/" */
326 if (!(*directory
) || (*directory
== PATH_SEP
)){
327 passwd
= getpwuid (geteuid ());
328 p
= (*directory
== PATH_SEP
) ? directory
+1 : "";
332 passwd
= getpwnam (directory
);
334 name
= xmalloc (p
- directory
+ 1, "tilde_expand");
335 strncpy (name
, directory
, p
- directory
);
336 name
[p
- directory
] = 0;
337 passwd
= getpwnam (name
);
342 /* If we can't figure the user name, return NULL */
346 len
= strlen (passwd
->pw_dir
) + strlen (p
) + 2;
347 directory
= xmalloc (len
, "tilde_expand");
348 strcpy (directory
, passwd
->pw_dir
);
349 strcat (directory
, PATH_SEP_STR
);
350 strcat (directory
, p
);
355 set_nonblocking (int fd
)
359 val
= fcntl (fd
, F_GETFL
, 0);
361 return fcntl (fd
, F_SETFL
, val
) != -1;
364 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
365 /* More than that would be unportable */
366 #define MAX_PIPE_SIZE 4096
368 static int error_pipe
[2]; /* File descriptors of error pipe */
369 static int old_error
; /* File descriptor of old standard error */
371 /* Creates a pipe to hold standard error for a later analysis. */
372 /* The pipe can hold 4096 bytes. Make sure no more is written */
373 /* or a deadlock might occur. */
374 void open_error_pipe (void)
376 if (pipe (error_pipe
) < 0){
377 message (0, _(" Warning "), _(" Pipe failed "));
380 if(old_error
< 0 || close(2) || dup (error_pipe
[1]) != 2){
381 message (0, _(" Warning "), _(" Dup failed "));
382 close (error_pipe
[0]);
383 close (error_pipe
[1]);
385 close (error_pipe
[1]);
388 void close_error_pipe (int error
, char *text
)
391 char msg
[MAX_PIPE_SIZE
];
402 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
);
406 close (error_pipe
[0]);
409 return; /* Just ignore error message */
411 if (len
== 0) return; /* Nothing to show */
413 /* Show message from pipe */
414 message (error
, title
, msg
);
416 /* Show given text and possible message from pipe */
417 message (error
, title
, " %s \n %s ", text
, msg
);
421 /* Checks for messages in the error pipe,
422 * closes the pipe and displays an error box if needed
424 void check_error_pipe (void)
426 char error
[MAX_PIPE_SIZE
];
429 while (len
< MAX_PIPE_SIZE
)
432 struct timeval timeout
;
433 FD_ZERO (&select_set
);
434 FD_SET (error_pipe
[0], &select_set
);
437 select (FD_SETSIZE
, &select_set
, 0, 0, &timeout
);
438 if (!FD_ISSET (0, &select_set
))
440 read (error_pipe
[0], error
+ len
, 1);
444 close (error_pipe
[0]);
447 message (0, _(" Warning "), error
);
450 static struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
452 /* INHANDLE is a result of some mc_open call to any vfs, this function
453 returns a normal handle (to be used with read) of a pipe for reading
454 of the output of COMMAND with arguments ... (must include argv[0] as
455 well) which gets as its input at most INLEN bytes from the INHANDLE
456 using mc_read. You have to call mc_doublepclose to close the returned
457 handle afterwards. If INLEN is -1, we read as much as we can :) */
458 int mc_doublepopen (int inhandle
, int inlen
, pid_t
*the_pid
, char *command
, ...)
460 int pipe0
[2], pipe1
[2];
463 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
464 #define is_a_pipe_fd(f) ((pipe0[0] == f) || (pipe0[1] == f) || (pipe1[0] == f) || (pipe1[1] == f))
466 pipe (pipe0
); pipe (pipe1
);
467 ignore
.sa_handler
= SIG_IGN
;
468 sigemptyset (&ignore
.sa_mask
);
471 sigaction (SIGINT
, &ignore
, &save_intr
);
472 sigaction (SIGQUIT
, &ignore
, &save_quit
);
473 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
475 switch (pid
= fork ()) {
480 sigaction (SIGINT
, &save_intr
, NULL
);
481 sigaction (SIGQUIT
, &save_quit
, NULL
);
483 switch (pid
= fork ()) {
494 port_shutdown_extra_fds ();
496 nulldevice
= open ("/dev/null", O_WRONLY
);
505 va_start (ap
, command
);
507 while ((args
[argno
++] = va_arg(ap
, char *)) != NULL
)
508 if (argno
== (MAXARGS
- 1)) {
513 execvp (command
, args
);
524 while ((i
= mc_read (inhandle
, buffer
,
525 (inlen
== -1 || inlen
> 8192)
526 ? 8192 : inlen
)) > 0) {
527 write (pipe0
[1], buffer
, i
);
535 while (waitpid (pid
, &i
, 0) < 0)
539 port_shutdown_extra_fds ();
554 int mc_doublepclose (int pipe
, pid_t pid
)
559 waitpid (pid
, &status
, 0);
561 waitpid (-1, NULL
, WNOHANG
);
562 #endif /* SCO_FLAVOR */
563 sigaction (SIGINT
, &save_intr
, NULL
);
564 sigaction (SIGQUIT
, &save_quit
, NULL
);
565 sigaction (SIGTSTP
, &save_stop
, NULL
);
570 /* Canonicalize path, and return a new path. Do everything in situ.
571 The new path differs from path in:
572 Multiple `/'s are collapsed to a single `/'.
573 Leading `./'s and trailing `/.'s are removed.
574 Trailing `/'s are removed.
575 Non-leading `../'s and trailing `..'s are handled by removing
576 portions of the path. */
577 char *canonicalize_pathname (char *path
)
582 stub_char
= (*path
== PATH_SEP
) ? PATH_SEP
: '.';
584 /* Walk along path looking for things to compact. */
590 while (path
[i
] && path
[i
] != PATH_SEP
)
595 /* If we didn't find any slashes, then there is nothing left to do. */
601 ** QNX accesses the directories of nodes on its peer-to-peer
602 ** network by prefixing their directories with "//[nid]".
603 ** We don't want to collapse two '/'s if they're at the start
604 ** of the path, followed by digits, and ending with a '/'.
606 if (start
== 0 && i
== 1)
609 char *q
= strchr(p
, PATH_SEP
);
614 if (!strcspn(p
, "0123456789"))
624 /* Handle multiple `/'s in a row. */
625 while (path
[i
] == PATH_SEP
)
628 if ((start
+ 1) != i
) {
629 strcpy (path
+ start
+ 1, path
+ i
);
633 /* Handle backquoted `/'. */
634 if (start
> 0 && path
[start
- 1] == '\\')
637 /* Check for trailing `/'. */
638 if (start
&& !path
[i
]) {
644 /* Check for `../', `./' or trailing `.' by itself. */
645 if (path
[i
] == '.') {
646 /* Handle trailing `.' by itself. */
651 if (path
[i
+ 1] == PATH_SEP
) {
652 strcpy (path
+ i
, path
+ i
+ 1);
657 /* Handle `../' or trailing `..' by itself.
658 Remove the previous ?/ part with the exception of
659 ../, which we should leave intact. */
660 if (path
[i
+ 1] == '.' && (path
[i
+ 2] == PATH_SEP
|| !path
[i
+ 2])) {
661 while (--start
> -1 && path
[start
] != PATH_SEP
);
662 if (!strncmp (path
+ start
+ 1, "../", 3))
664 strcpy (path
+ start
+ 1, path
+ i
+ 2);
679 int gettimeofday( struct timeval
* tv
, struct timezone
* tz
)
687 l
= localtime(&tb
.time
);
688 tv
->tv_sec
= l
->tm_sec
;
689 tv
->tv_usec
= (long) tb
.millitm
;
692 #endif /* SCO_FLAVOR */
696 /* The following piece of code was copied from the GNU C Library */
697 /* And is provided here for nextstep who lacks putenv */
699 extern char **environ
;
702 #define __environ environ
706 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
708 putenv (const char *string
)
710 const char *const name_end
= strchr (string
, '=');
711 register size_t size
;
714 if (name_end
== NULL
){
715 /* Remove the variable from the environment. */
716 size
= strlen (string
);
717 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
718 if (!strncmp (*ep
, string
, size
) && (*ep
)[size
] == '='){
719 while (ep
[1] != NULL
){
729 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
730 if (!strncmp (*ep
, string
, name_end
- string
) &&
731 (*ep
)[name_end
- string
] == '=')
737 static char **last_environ
= NULL
;
738 char **new_environ
= (char **) malloc ((size
+ 2) * sizeof (char *));
739 if (new_environ
== NULL
)
741 (void) memcpy ((void *) new_environ
, (void *) __environ
,
742 size
* sizeof (char *));
743 new_environ
[size
] = (char *) string
;
744 new_environ
[size
+ 1] = NULL
;
745 if (last_environ
!= NULL
)
746 free ((void *) last_environ
);
747 last_environ
= new_environ
;
748 __environ
= new_environ
;
751 *ep
= (char *) string
;
755 #endif /* !HAVE_PUTENV */
757 void my_statfs (struct my_statfs
*myfs_stats
, char *path
)
762 struct mount_entry
*entry
= NULL
;
763 struct mount_entry
*temp
= mount_list
;
764 struct fs_usage fs_use
;
767 i
= strlen (temp
->me_mountdir
);
768 if (i
> len
&& (strncmp (path
, temp
->me_mountdir
, i
) == 0))
769 if (!entry
|| (path
[i
] == PATH_SEP
|| path
[i
] == 0)){
773 temp
= temp
->me_next
;
777 get_fs_usage (entry
->me_mountdir
, &fs_use
);
779 myfs_stats
->type
= entry
->me_dev
;
780 myfs_stats
->typename
= entry
->me_type
;
781 myfs_stats
->mpoint
= entry
->me_mountdir
;
782 myfs_stats
->device
= entry
->me_devname
;
783 myfs_stats
->avail
= getuid () ? fs_use
.fsu_bavail
/2 : fs_use
.fsu_bfree
/2;
784 myfs_stats
->total
= fs_use
.fsu_blocks
/2;
785 myfs_stats
->nfree
= fs_use
.fsu_ffree
;
786 myfs_stats
->nodes
= fs_use
.fsu_files
;
789 #if defined(NO_INFOMOUNT) && defined(__QNX__)
791 ** This is the "other side" of the hack to read_filesystem_list() in
793 ** It's not the most efficient approach, but consumes less memory. It
794 ** also accomodates QNX's ability to mount filesystems on the fly.
796 struct mount_entry
*entry
;
797 struct fs_usage fs_use
;
799 if ((entry
= read_filesystem_list(0, 0)) != NULL
)
801 get_fs_usage(entry
->me_mountdir
, &fs_use
);
803 myfs_stats
->type
= entry
->me_dev
;
804 myfs_stats
->typename
= entry
->me_type
;
805 myfs_stats
->mpoint
= entry
->me_mountdir
;
806 myfs_stats
->device
= entry
->me_devname
;
808 myfs_stats
->avail
= fs_use
.fsu_bfree
/ 2;
809 myfs_stats
->total
= fs_use
.fsu_blocks
/ 2;
810 myfs_stats
->nfree
= fs_use
.fsu_ffree
;
811 myfs_stats
->nodes
= fs_use
.fsu_files
;
816 myfs_stats
->type
= 0;
817 myfs_stats
->mpoint
= "unknown";
818 myfs_stats
->device
= "unknown";
819 myfs_stats
->avail
= 0;
820 myfs_stats
->total
= 0;
821 myfs_stats
->nfree
= 0;
822 myfs_stats
->nodes
= 0;
826 #ifdef HAVE_GET_PROCESS_STATS
827 # include <sys/procstats.h>
829 int gettimeofday (struct timeval
*tp
, void *tzp
)
831 return get_process_stats(tp
, PS_SELF
, 0, 0);
836 /* Define this only for SCO */
838 #ifndef HAVE_SOCKETPAIR
841 The code for s_pipe function is adapted from Section 7.9
842 of the "UNIX Network Programming" by W. Richard Stevens,
843 published by Prentice Hall, ISBN 0-13-949876-1
844 (c) 1990 by P T R Prentice Hall
846 It is used to implement socketpair function for SVR3 systems
850 #include <sys/types.h>
851 #include <sys/stream.h> /* defines queue_t */
852 #include <stropts.h> /* defines struct strtdinsert */
855 #define SPX_DEVICE "/dev/spx"
856 #define S_PIPE_HANDLE_ERRNO 1
857 /* if the above is defined to 1, we will attempt to
858 save and restore errno to indicate failure
859 reason to the caller;
860 Please note that this will not work in environments
861 where errno is not just an integer
864 #if S_PIPE_HANDLE_ERRNO
866 /* This is for "extern int errno;" */
869 /* s_pipe returns 0 if OK, -1 on error */
870 /* two file descriptors are returned */
871 int s_pipe(int fd
[2])
873 struct strfdinsert ins
; /* stream I_FDINSERT ioctl format */
875 #if S_PIPE_HANDLE_ERRNO
879 * First open the stream clone device "dev/spx" twice,
880 * obtaining the two file descriptors
883 if ( (fd
[0] = open(SPX_DEVICE
, O_RDWR
)) < 0)
885 if ( (fd
[1] = open(SPX_DEVICE
, O_RDWR
)) < 0) {
886 #if S_PIPE_HANDLE_ERRNO
890 #if S_PIPE_HANDLE_ERRNO
897 * Now link these two stream together with an I_FDINSERT ioctl.
900 ins
.ctlbuf
.buf
= (char *) &pointer
; /* no control information, just the pointer */
901 ins
.ctlbuf
.maxlen
= sizeof pointer
;
902 ins
.ctlbuf
.len
= sizeof pointer
;
903 ins
.databuf
.buf
= (char *) 0; /* no data to be sent */
904 ins
.databuf
.maxlen
= 0;
905 ins
.databuf
.len
= -1; /* magic: must be -1 rather than 0 for stream pipes */
907 ins
.fildes
= fd
[1]; /* the fd to connect with fd[0] */
908 ins
.flags
= 0; /* nonpriority message */
909 ins
.offset
= 0; /* offset of pointer in control buffer */
911 if (ioctl(fd
[0], I_FDINSERT
, (char *) &ins
) < 0) {
912 #if S_PIPE_HANDLE_ERRNO
917 #if S_PIPE_HANDLE_ERRNO
922 /* all is OK if we came here, indicate success */
926 int socketpair(int dummy1
, int dummy2
, int dummy3
, int fd
[2])
931 #endif /* ifndef HAVE_SOCKETPAIR */
932 #endif /* ifdef USE_NETCODE */
933 #endif /* SCO_FLAVOR */