Just a little correction at the it.po file.
[midnight-commander.git] / src / utilunix.c
blob1daa5862807eb1078ccd7069d0ff17e2c24e4902
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 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #ifdef HAVE_UNISTD_H
25 # include <unistd.h>
26 #endif
27 #include <signal.h> /* struct sigaction */
28 #include <limits.h> /* INT_MAX */
29 #include <sys/stat.h>
30 #include <stdarg.h>
31 #include <errno.h> /* errno */
32 #include <string.h>
33 #include <ctype.h>
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
36 #endif
38 #include "global.h"
39 #include "fsusage.h"
40 #include "mountlist.h"
41 #include "wtools.h" /* message() */
43 struct sigaction startup_handler;
45 /* uid of the MC user */
46 static uid_t current_user_uid = -1;
47 /* List of the gids of the user */
48 static GTree *current_user_gid = NULL;
50 /* Helper function to compare 2 gids */
51 static gint
52 mc_gid_compare (gconstpointer v, gconstpointer v2)
54 return ((GPOINTER_TO_UINT(v) > GPOINTER_TO_UINT(v2)) ? 1 :
55 (GPOINTER_TO_UINT(v) < GPOINTER_TO_UINT(v2)) ? -1 : 0);
58 /* Helper function to delete keys of the gids tree */
59 static gint
60 mc_gid_destroy (gpointer key, gpointer value, gpointer data)
62 g_free (value);
64 return FALSE;
67 /* This function initialize global GTree with the gids of groups,
68 to which user belongs. Tree also store corresponding string
69 with the name of the group.
70 FIXME: Do we need this names at all? If not, we can simplify
71 initialization by eliminating g_strdup's.
73 void init_groups (void)
75 int i;
76 struct passwd *pwd;
77 struct group *grp;
79 current_user_uid = getuid ();
80 pwd = getpwuid (current_user_uid);
81 g_return_if_fail (pwd != NULL);
83 current_user_gid = g_tree_new (mc_gid_compare);
85 /* Put user's primary group first. */
86 if ((grp = getgrgid (pwd->pw_gid)) != NULL) {
87 g_tree_insert (current_user_gid,
88 GUINT_TO_POINTER ((int) grp->gr_gid),
89 g_strdup (grp->gr_name));
92 setgrent ();
93 while ((grp = getgrent ()) != NULL) {
94 for (i = 0; grp->gr_mem[i]; i++) {
95 if (!strcmp (pwd->pw_name, grp->gr_mem[i]) &&
96 !g_tree_lookup (current_user_gid,
97 GUINT_TO_POINTER ((int) grp->gr_gid))) {
98 g_tree_insert (current_user_gid,
99 GUINT_TO_POINTER ((int) grp->gr_gid),
100 g_strdup (grp->gr_name));
101 break;
105 endgrent ();
108 /* Return the index of the permissions triplet */
110 get_user_permissions (struct stat *buf) {
112 if (buf->st_uid == current_user_uid || current_user_uid == 0)
113 return 0;
115 if (current_user_gid && g_tree_lookup (current_user_gid,
116 GUINT_TO_POINTER((int) buf->st_gid)))
117 return 1;
119 return 2;
122 /* Completely destroys the gids tree */
123 void
124 destroy_groups (void)
126 g_tree_traverse (current_user_gid, mc_gid_destroy, G_POST_ORDER, NULL);
127 g_tree_destroy (current_user_gid);
130 #define UID_CACHE_SIZE 200
131 #define GID_CACHE_SIZE 30
133 typedef struct {
134 int index;
135 char *string;
136 } int_cache;
138 static int_cache uid_cache [UID_CACHE_SIZE];
139 static int_cache gid_cache [GID_CACHE_SIZE];
141 static char *i_cache_match (int id, int_cache *cache, int size)
143 int i;
145 for (i = 0; i < size; i++)
146 if (cache [i].index == id)
147 return cache [i].string;
148 return 0;
151 static void i_cache_add (int id, int_cache *cache, int size, char *text,
152 int *last)
154 if (cache [*last].string)
155 g_free (cache [*last].string);
156 cache [*last].string = g_strdup (text);
157 cache [*last].index = id;
158 *last = ((*last)+1) % size;
161 char *get_owner (int uid)
163 struct passwd *pwd;
164 static char ibuf [10];
165 char *name;
166 static int uid_last;
168 if ((name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE)) != NULL)
169 return name;
171 pwd = getpwuid (uid);
172 if (pwd){
173 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
174 return pwd->pw_name;
176 else {
177 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
178 return ibuf;
182 char *get_group (int gid)
184 struct group *grp;
185 static char gbuf [10];
186 char *name;
187 static int gid_last;
189 if ((name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE)) != NULL)
190 return name;
192 grp = getgrgid (gid);
193 if (grp){
194 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
195 return grp->gr_name;
196 } else {
197 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
198 return gbuf;
202 /* Since ncurses uses a handler that automatically refreshes the */
203 /* screen after a SIGCONT, and we don't want this behavior when */
204 /* spawning a child, we save the original handler here */
205 void save_stop_handler (void)
207 sigaction (SIGTSTP, NULL, &startup_handler);
210 int my_system (int flags, const char *shell, const char *command)
212 struct sigaction ignore, save_intr, save_quit, save_stop;
213 pid_t pid;
214 int status = 0;
216 ignore.sa_handler = SIG_IGN;
217 sigemptyset (&ignore.sa_mask);
218 ignore.sa_flags = 0;
220 sigaction (SIGINT, &ignore, &save_intr);
221 sigaction (SIGQUIT, &ignore, &save_quit);
223 /* Restore the original SIGTSTP handler, we don't want ncurses' */
224 /* handler messing the screen after the SIGCONT */
225 sigaction (SIGTSTP, &startup_handler, &save_stop);
227 if ((pid = fork ()) < 0){
228 fprintf (stderr, "\n\nfork () = -1\n");
229 return -1;
231 if (pid == 0){
232 signal (SIGINT, SIG_DFL);
233 signal (SIGQUIT, SIG_DFL);
234 signal (SIGTSTP, SIG_DFL);
235 signal (SIGCHLD, SIG_DFL);
237 if (flags & EXECUTE_AS_SHELL)
238 execl (shell, shell, "-c", command, NULL);
239 else
240 execlp (shell, shell, command, NULL);
242 _exit (127); /* Exec error */
243 } else {
244 while (waitpid (pid, &status, 0) < 0)
245 if (errno != EINTR){
246 status = -1;
247 break;
250 sigaction (SIGINT, &save_intr, NULL);
251 sigaction (SIGQUIT, &save_quit, NULL);
252 sigaction (SIGTSTP, &save_stop, NULL);
254 #ifdef SCO_FLAVOR
255 waitpid(-1, NULL, WNOHANG);
256 #endif /* SCO_FLAVOR */
258 return WEXITSTATUS(status);
261 /* Returns a newly allocated string, if directory does not exist, return 0 */
262 char *tilde_expand (const char *directory)
264 struct passwd *passwd;
265 const char *p;
266 char *name;
268 if (*directory != '~')
269 return g_strdup (directory);
271 directory++;
273 p = strchr (directory, PATH_SEP);
275 /* d = "~" or d = "~/" */
276 if (!(*directory) || (*directory == PATH_SEP)){
277 passwd = getpwuid (geteuid ());
278 p = (*directory == PATH_SEP) ? directory+1 : "";
279 } else {
280 if (!p){
281 passwd = getpwnam (directory);
282 } else {
283 name = g_malloc (p - directory + 1);
284 strncpy (name, directory, p - directory);
285 name [p - directory] = 0;
286 passwd = getpwnam (name);
287 g_free (name);
291 /* If we can't figure the user name, return NULL */
292 if (!passwd)
293 return 0;
295 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, p, NULL);
300 * Return the directory where mc should keep its temporary files.
301 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}-$USER"
302 * When called the first time, the directory is created if needed.
303 * The first call should be done early, since we are using fprintf()
304 * and not message() to report possible problems.
306 const char *
307 mc_tmpdir (void)
309 static char buffer[64];
310 static const char *tmpdir;
311 const char *sys_tmp;
312 struct passwd *pwd;
313 struct stat st;
314 const char *error = NULL;
316 /* Check if already initialized */
317 if (tmpdir)
318 return tmpdir;
320 sys_tmp = getenv ("TMPDIR");
321 if (!sys_tmp) {
322 sys_tmp = TMPDIR_DEFAULT;
325 pwd = getpwuid (getuid ());
326 g_snprintf (buffer, sizeof (buffer), "%s/mc-%s", sys_tmp,
327 pwd->pw_name);
328 canonicalize_pathname (buffer);
330 if (lstat (buffer, &st) == 0) {
331 /* Sanity check for existing directory */
332 if (!S_ISDIR (st.st_mode))
333 error = _("%s is not a directory\n");
334 else if (st.st_uid != getuid ())
335 error = _("Directory %s is not owned by you\n");
336 else if (((st.st_mode & 0777) != 0700)
337 && (chmod (buffer, 0700) != 0))
338 error = _("Cannot set correct permissions for directory %s\n");
339 } else {
340 /* Need to create directory */
341 if (mkdir (buffer, S_IRWXU) != 0) {
342 fprintf (stderr,
343 _("Cannot create temporary directory %s: %s\n"),
344 buffer, unix_error_string (errno));
345 error = "";
349 if (!error) {
350 tmpdir = buffer;
351 } else {
352 int test_fd;
353 char *test_fn;
354 int fallback_ok = 0;
356 if (*error)
357 fprintf (stderr, error, buffer);
359 /* Test if sys_tmp is suitable for temporary files */
360 tmpdir = sys_tmp;
361 test_fd = mc_mkstemps (&test_fn, "mctest", NULL);
362 if (test_fd != -1) {
363 close (test_fd);
364 test_fd = open (test_fn, O_RDONLY);
365 if (test_fd != -1) {
366 close (test_fd);
367 unlink (test_fn);
368 fallback_ok = 1;
372 if (fallback_ok) {
373 fprintf (stderr, _("Temporary files will be created in %s\n"),
374 sys_tmp);
375 } else {
376 fprintf (stderr, _("Temporary files will not be created\n"));
377 tmpdir = "/dev/null/";
380 fprintf (stderr, "%s\n", _("Press any key to continue..."));
381 getc (stdin);
384 return tmpdir;
388 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
389 /* More than that would be unportable */
390 #define MAX_PIPE_SIZE 4096
392 static int error_pipe[2]; /* File descriptors of error pipe */
393 static int old_error; /* File descriptor of old standard error */
395 /* Creates a pipe to hold standard error for a later analysis. */
396 /* The pipe can hold 4096 bytes. Make sure no more is written */
397 /* or a deadlock might occur. */
398 void open_error_pipe (void)
400 if (pipe (error_pipe) < 0){
401 message (0, _("Warning"), _(" Pipe failed "));
403 old_error = dup (2);
404 if(old_error < 0 || close(2) || dup (error_pipe[1]) != 2){
405 message (0, _("Warning"), _(" Dup failed "));
406 close (error_pipe[0]);
407 close (error_pipe[1]);
409 close (error_pipe[1]);
413 * Returns true if an error was displayed
414 * error: -1 - ignore errors, 0 - display warning, 1 - display error
415 * text is prepended to the error message from the pipe
418 close_error_pipe (int error, char *text)
420 char *title;
421 char msg[MAX_PIPE_SIZE];
422 int len = 0;
424 if (error)
425 title = MSG_ERROR;
426 else
427 title = _("Warning");
428 if (old_error >= 0){
429 close (2);
430 dup (old_error);
431 close (old_error);
432 len = read (error_pipe[0], msg, MAX_PIPE_SIZE);
434 if (len >= 0)
435 msg[len] = 0;
436 close (error_pipe[0]);
438 if (error < 0)
439 return 0; /* Just ignore error message */
440 if (text == NULL){
441 if (len <= 0)
442 return 0; /* Nothing to show */
444 /* Show message from pipe */
445 message (error, title, "%s", msg);
446 } else {
447 /* Show given text and possible message from pipe */
448 message (error, title, " %s \n %s ", text, msg);
450 return 1;
453 /* Checks for messages in the error pipe,
454 * closes the pipe and displays an error box if needed
456 void check_error_pipe (void)
458 char error[MAX_PIPE_SIZE];
459 int len = 0;
460 if (old_error >= 0){
461 while (len < MAX_PIPE_SIZE)
463 fd_set select_set;
464 struct timeval timeout;
465 FD_ZERO (&select_set);
466 FD_SET (error_pipe[0], &select_set);
467 timeout.tv_sec = 0;
468 timeout.tv_usec = 0;
469 select (error_pipe[0] + 1, &select_set, 0, 0, &timeout);
470 if (!FD_ISSET (error_pipe[0], &select_set))
471 break;
472 read (error_pipe[0], error + len, 1);
473 len ++;
475 error[len] = 0;
476 close (error_pipe[0]);
478 if (len > 0)
479 message (0, _("Warning"), "%s", error);
482 static struct sigaction ignore, save_intr, save_quit, save_stop;
484 /* INHANDLE is a result of some mc_open call to any vfs, this function
485 returns a normal handle (to be used with read) of a pipe for reading
486 of the output of COMMAND with arguments ... (must include argv[0] as
487 well) which gets as its input at most INLEN bytes from the INHANDLE
488 using mc_read. You have to call mc_doublepclose to close the returned
489 handle afterwards. If INLEN is -1, we read as much as we can :) */
490 int mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
492 int pipe0 [2], pipe1 [2];
493 pid_t pid;
495 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
497 pipe (pipe0); pipe (pipe1);
498 ignore.sa_handler = SIG_IGN;
499 sigemptyset (&ignore.sa_mask);
500 ignore.sa_flags = 0;
502 sigaction (SIGINT, &ignore, &save_intr);
503 sigaction (SIGQUIT, &ignore, &save_quit);
504 sigaction (SIGTSTP, &startup_handler, &save_stop);
506 switch (pid = fork ()) {
507 case -1:
508 closepipes ();
509 return -1;
510 case 0: {
511 sigaction (SIGINT, &save_intr, NULL);
512 sigaction (SIGQUIT, &save_quit, NULL);
513 switch (pid = fork ()) {
514 case -1:
515 closepipes ();
516 _exit (1);
517 case 0: {
518 #define MAXARGS 16
519 int argno;
520 char *args[MAXARGS];
521 va_list ap;
522 int nulldevice;
524 nulldevice = open ("/dev/null", O_WRONLY);
525 close (0);
526 dup (pipe0 [0]);
527 close (1);
528 dup (pipe1 [1]);
529 close (2);
530 dup (nulldevice);
531 close (nulldevice);
532 closepipes ();
533 va_start (ap, command);
534 argno = 0;
535 while ((args[argno++] = va_arg(ap, char *)) != NULL)
536 if (argno == (MAXARGS - 1)) {
537 args[argno] = NULL;
538 break;
540 va_end (ap);
541 execvp (command, args);
543 /* If we are here exec has failed */
544 _exit (0);
546 default:
548 char buffer [8192];
549 int i;
551 close (pipe0 [0]);
552 close (pipe1 [0]);
553 close (pipe1 [1]);
554 while ((i = mc_read (inhandle, buffer,
555 (inlen == -1 || inlen > 8192)
556 ? 8192 : inlen)) > 0) {
557 write (pipe0 [1], buffer, i);
558 if (inlen != -1) {
559 inlen -= i;
560 if (!inlen)
561 break;
564 close (inhandle);
565 close (pipe0 [1]);
566 while (waitpid (pid, &i, 0) < 0)
567 if (errno != EINTR)
568 break;
570 _exit (i);
574 default:
575 *the_pid = pid;
576 break;
578 close (pipe0 [0]);
579 close (pipe0 [1]);
580 close (pipe1 [1]);
581 return pipe1 [0];
584 int mc_doublepclose (int pipe, pid_t pid)
586 int status = 0;
588 close (pipe);
589 waitpid (pid, &status, 0);
590 #ifdef SCO_FLAVOR
591 waitpid (-1, NULL, WNOHANG);
592 #endif /* SCO_FLAVOR */
593 sigaction (SIGINT, &save_intr, NULL);
594 sigaction (SIGQUIT, &save_quit, NULL);
595 sigaction (SIGTSTP, &save_stop, NULL);
597 return status;
600 /* Canonicalize path, and return a new path. Do everything in situ.
601 The new path differs from path in:
602 Multiple `/'s are collapsed to a single `/'.
603 Leading `./'s and trailing `/.'s are removed.
604 Trailing `/'s are removed.
605 Non-leading `../'s and trailing `..'s are handled by removing
606 portions of the path. */
607 char *
608 canonicalize_pathname (char *path)
610 char *p, *s;
611 int len;
613 if (!path[0] || !path[1])
614 return path;
616 /* Collapse multiple slashes */
617 p = path;
618 while (*p) {
619 if (p[0] == PATH_SEP && p[1] == PATH_SEP) {
620 s = p + 1;
621 while (*(++s) == PATH_SEP);
622 strcpy (p + 1, s);
624 p++;
627 /* Collapse "/./" -> "/" */
628 p = path;
629 while (*p) {
630 if (p[0] == PATH_SEP && p[1] == '.' && p[2] == PATH_SEP)
631 strcpy (p, p + 2);
632 else
633 p++;
636 /* Remove trailing slashes */
637 p = path + strlen (path) - 1;
638 while (p > path && *p == PATH_SEP)
639 *p-- = 0;
641 /* Remove leading "./" */
642 if (path[0] == '.' && path[1] == PATH_SEP) {
643 if (path[2] == 0) {
644 path[1] = 0;
645 return path;
646 } else {
647 strcpy (path, path + 2);
651 /* Remove trailing "/" or "/." */
652 len = strlen (path);
653 if (len < 2)
654 return path;
655 if (path[len - 1] == PATH_SEP) {
656 path[len - 1] = 0;
657 } else {
658 if (path[len - 1] == '.' && path[len - 2] == PATH_SEP) {
659 if (len == 2) {
660 path[1] = 0;
661 return path;
662 } else {
663 path[len - 2] = 0;
668 /* Collapse "/.." with the previous part of path */
669 p = path;
670 while (p[0] && p[1] && p[2]) {
671 if ((p[0] != PATH_SEP || p[1] != '.' || p[2] != '.')
672 || (p[3] != PATH_SEP && p[3] != 0)) {
673 p++;
674 continue;
677 /* search for the previous token */
678 s = p - 1;
679 while (s >= path && *s != PATH_SEP)
680 s--;
682 s++;
684 /* If the previous token is "..", we cannot collapse it */
685 if (s[0] == '.' && s[1] == '.' && s + 2 == p) {
686 p += 3;
687 continue;
690 if (p[3] != 0) {
691 if (s == path && *s == PATH_SEP) {
692 /* "/../foo" -> "/foo" */
693 strcpy (s + 1, p + 4);
694 } else {
695 /* "token/../foo" -> "foo" */
696 strcpy (s, p + 4);
698 p = (s > path) ? s - 1 : s;
699 continue;
702 /* trailing ".." */
703 if (s == path) {
704 /* "token/.." -> "." */
705 if (path[0] != PATH_SEP) {
706 path[0] = '.';
708 path[1] = 0;
709 } else {
710 /* "foo/token/.." -> "foo" */
711 if (s == path + 1)
712 s[0] = 0;
713 else
714 s[-1] = 0;
715 break;
718 return path;
721 return path;
724 #ifdef SCO_FLAVOR
725 int gettimeofday( struct timeval * tv, struct timezone * tz)
727 struct timeb tb;
728 struct tm * l;
730 ftime( &tb );
731 if (errno == EFAULT)
732 return -1;
733 l = localtime(&tb.time);
734 tv->tv_sec = l->tm_sec;
735 tv->tv_usec = (long) tb.millitm;
736 return 0;
738 #endif /* SCO_FLAVOR */
740 #ifdef HAVE_GET_PROCESS_STATS
741 # include <sys/procstats.h>
743 int gettimeofday (struct timeval *tp, void *tzp)
745 return get_process_stats(tp, PS_SELF, 0, 0);
747 #endif /* HAVE_GET_PROCESS_STATS */
749 #ifndef HAVE_PUTENV
751 /* The following piece of code was copied from the GNU C Library */
752 /* And is provided here for nextstep who lacks putenv */
754 extern char **environ;
756 #ifndef HAVE_GNU_LD
757 #define __environ environ
758 #endif
761 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
763 putenv (const char *string)
765 const char *const name_end = strchr (string, '=');
766 register size_t size;
767 register char **ep;
769 if (name_end == NULL){
770 /* Remove the variable from the environment. */
771 size = strlen (string);
772 for (ep = __environ; *ep != NULL; ++ep)
773 if (!strncmp (*ep, string, size) && (*ep)[size] == '='){
774 while (ep[1] != NULL){
775 ep[0] = ep[1];
776 ++ep;
778 *ep = NULL;
779 return 0;
783 size = 0;
784 for (ep = __environ; *ep != NULL; ++ep)
785 if (!strncmp (*ep, string, name_end - string) &&
786 (*ep)[name_end - string] == '=')
787 break;
788 else
789 ++size;
791 if (*ep == NULL){
792 static char **last_environ = NULL;
793 char **new_environ = g_new (char *, size + 2);
794 if (new_environ == NULL)
795 return -1;
796 (void) memcpy ((void *) new_environ, (void *) __environ,
797 size * sizeof (char *));
798 new_environ[size] = (char *) string;
799 new_environ[size + 1] = NULL;
800 if (last_environ != NULL)
801 g_free ((void *) last_environ);
802 last_environ = new_environ;
803 __environ = new_environ;
805 else
806 *ep = (char *) string;
808 return 0;
810 #endif /* !HAVE_PUTENV */
812 #ifdef SCO_FLAVOR
813 /* Define this only for SCO */
814 #ifdef USE_NETCODE
815 #ifndef HAVE_SOCKETPAIR
818 The code for s_pipe function is adapted from Section 7.9
819 of the "UNIX Network Programming" by W. Richard Stevens,
820 published by Prentice Hall, ISBN 0-13-949876-1
821 (c) 1990 by P T R Prentice Hall
823 It is used to implement socketpair function for SVR3 systems
824 that lack it.
827 #include <sys/types.h>
828 #include <sys/stream.h> /* defines queue_t */
829 #include <stropts.h> /* defines struct strtdinsert */
831 #define SPX_DEVICE "/dev/spx"
832 #define S_PIPE_HANDLE_ERRNO 1
833 /* if the above is defined to 1, we will attempt to
834 save and restore errno to indicate failure
835 reason to the caller;
836 Please note that this will not work in environments
837 where errno is not just an integer
840 #if S_PIPE_HANDLE_ERRNO
841 #include <errno.h>
842 /* This is for "extern int errno;" */
843 #endif
845 /* s_pipe returns 0 if OK, -1 on error */
846 /* two file descriptors are returned */
847 static int s_pipe(int fd[2])
849 struct strfdinsert ins; /* stream I_FDINSERT ioctl format */
850 queue_t *pointer;
851 #if S_PIPE_HANDLE_ERRNO
852 int err_save;
853 #endif
855 * First open the stream clone device "dev/spx" twice,
856 * obtaining the two file descriptors
859 if ( (fd[0] = open(SPX_DEVICE, O_RDWR)) < 0)
860 return -1;
861 if ( (fd[1] = open(SPX_DEVICE, O_RDWR)) < 0) {
862 #if S_PIPE_HANDLE_ERRNO
863 err_save = errno;
864 #endif
865 close(fd[0]);
866 #if S_PIPE_HANDLE_ERRNO
867 errno = err_save;
868 #endif
869 return -1;
873 * Now link these two stream together with an I_FDINSERT ioctl.
876 ins.ctlbuf.buf = (char *) &pointer; /* no control information, just the pointer */
877 ins.ctlbuf.maxlen = sizeof pointer;
878 ins.ctlbuf.len = sizeof pointer;
879 ins.databuf.buf = (char *) 0; /* no data to be sent */
880 ins.databuf.maxlen = 0;
881 ins.databuf.len = -1; /* magic: must be -1 rather than 0 for stream pipes */
883 ins.fildes = fd[1]; /* the fd to connect with fd[0] */
884 ins.flags = 0; /* nonpriority message */
885 ins.offset = 0; /* offset of pointer in control buffer */
887 if (ioctl(fd[0], I_FDINSERT, (char *) &ins) < 0) {
888 #if S_PIPE_HANDLE_ERRNO
889 err_save = errno;
890 #endif
891 close(fd[0]);
892 close(fd[1]);
893 #if S_PIPE_HANDLE_ERRNO
894 errno = err_save;
895 #endif
896 return -1;
898 /* all is OK if we came here, indicate success */
899 return 0;
902 int socketpair(int dummy1, int dummy2, int dummy3, int fd[2])
904 return s_pipe(fd);
907 #endif /* ifndef HAVE_SOCKETPAIR */
908 #endif /* ifdef USE_NETCODE */
909 #endif /* SCO_FLAVOR */