forgotten commit. disabled until egl is adapted.
[AROS-Contrib.git] / regina / os_unx.c
blobad4c8d8d747b26c8f39760fe23653d96ab50b861
1 /*
2 * The Regina Rexx Interpreter
3 * Copyright (C) 2005 Florian Grosse-Coosmann
5 * System interfacing functions for unix and equivalent systems
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library 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 GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "rexx.h"
23 #include "utsname.h"
25 #include <stdlib.h>
26 #include <string.h>
28 #if defined(HAVE_STRING_H)
29 # include <stdio.h>
30 #endif
32 #if defined(DOS)
33 # ifdef _POSIX_SOURCE /* emulation system? */
34 # undef _POSIX_SOURCE
35 # endif
36 # include <dos.h>
37 #endif
39 #if defined(HAVE_ASSERT_H)
40 # include <assert.h>
41 #endif
43 #if defined(HAVE_UNISTD_H)
44 # include <unistd.h>
45 #endif
47 #if defined(HAVE_SYS_STAT_H)
48 # include <sys/stat.h>
49 #endif
51 #if defined(HAVE_SYS_WAIT_H)
52 # include <sys/wait.h>
53 #endif
55 #if defined(HAVE_FCNTL_H)
56 # include <fcntl.h>
57 #endif
59 #if defined(HAVE_SYS_FCNTL_H)
60 # include <sys/fcntl.h>
61 #endif
63 #if defined(HAVE_PROCESS_H)
64 # include <process.h>
65 #endif
67 #if defined(HAVE_SHARE_H)
68 # include <share.h>
69 #endif
71 #if defined(HAVE_TIME_H)
72 # include <time.h>
73 #endif
75 #if defined(HAVE_ERRNO_H)
76 # include <errno.h>
77 #endif
79 #if defined(HAVE_SIGNAL_H)
80 # include <signal.h>
81 #endif
83 #if defined(HAVE_ASSERT_H)
84 # include <assert.h>
85 #endif
87 #if defined(HAVE_SYS_UTSNAME_H)
88 # include <sys/utsname.h>
89 #endif
91 #if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
92 # if defined(HAVE_POLL_H)
93 # include <poll.h>
94 # else
95 # include <sys/poll.h>
96 # endif
97 /* implement a simple(!) wait mechanism for a max. of 3 handles */
98 typedef struct {
99 const tsd_t *TSD;
100 struct pollfd _p[3] ;
101 int _p_cnt ;
102 } AsyncInfo;
103 #elif defined(HAVE_SYS_SELECT_H) || defined(SELECT_IN_TIME_H) || defined(HAVE_SYS_SOCKET_H)
104 # if defined(HAVE_SYS_SELECT_H)
105 # include <sys/select.h>
106 # elif defined(HAVE_SYS_SOCKET_H)
107 # include <sys/socket.h>
108 # else
109 # include <time.h>
110 # endif
111 /* implement a simple(!) wait mechanism for a max. of 3 handles */
112 typedef struct {
113 const tsd_t *TSD;
114 fd_set _p_in;
115 fd_set _p_out;
116 int _p_max ;
117 } AsyncInfo;
118 #endif
120 #if defined(HAVE_SYS_RESOURCE_H)
121 # include <sys/resource.h>
122 #endif
124 static int Unx_setenv( const char *name, const char *value )
126 #if defined(HAVE_SETENV)
127 setenv( name, value, 1 );
128 return 1;
129 #else
130 char *cmd;
132 if (value == NULL)
133 value = "";
134 cmd = malloc( strlen(name) + strlen(value) + 2);
135 sprintf( cmd, "%s=%s", name, value );
136 putenv( cmd );
137 free( cmd );
138 return 1;
139 # endif
141 /* MaxFiles returns the maximum number of files which can be addressed by a
142 * single process. We guess the result if we can't determine it.
144 #if defined(__QNX__) && !defined(__QNXNTO__)
145 #include <sys/osinfo.h>
146 static int MaxFiles(void)
148 * returns the maximum number of files which can be addressed by a single
149 * process. We guess the result if we can't determine it.
152 struct _osinfo osdata;
153 if ( qnx_osinfo( 0, &osdata ) != -1 )
155 return osdata.num_fds[1];
157 else
159 return 256;
162 #else
163 static int MaxFiles(void)
165 int rlmax = INT_MAX; /* resource's limit */
166 int scmax = INT_MAX; /* sysconf limit */
168 #ifdef _SC_OPEN_MAX
169 scmax = sysconf(_SC_OPEN_MAX); /* systemwide maximum */
170 #endif
172 #if defined(HAVE_SYS_RESOURCE_H) && defined(RLIMIT_OFILE)
173 /* user's limit might be decreased by himself: */
175 struct rlimit rl;
177 if (getrlimit(RLIMIT_OFILE,&rl) == 0)
178 if ((unsigned) rl.rlim_cur < (unsigned) INT_MAX)
179 rlmax = (int) rl.rlim_cur;
181 #endif
183 if (rlmax < scmax) /* map rlmax to scmax */
184 scmax = rlmax;
185 if (scmax != INT_MAX) /* either getrlimit or sysconf valid? */
186 return(scmax);
188 #ifdef POSIX_OPEN_MAX
189 /* maybe, we have a hardcoded limit */
190 if (POSIX_OPEN_MAX != INT_MAX) /* shall work in most cases */
191 return(POSIX_OPEN_MAX);
192 #endif
194 return(256); /* just a guess */
196 #endif
199 * fork_exec spawns a new process with the given commandline.
200 * it returns -1 on error (errno set), 0 on process start error (rcode set),
201 * a process descriptor otherwise.
202 * Basically this is a child process and we run in the child's environment
203 * after the first few lines. The setup hasn't been done and the command needs
204 * to be started.
205 * Redirection takes place if one of the handles env->input.hdls[0],
206 * env->output.hdls[1] or env->error.hdls[1] is defined. Other handles (except
207 * standard handles) are closed. env->subtype must be a SUBENVIR_... constant.
208 * cmdline is the whole command line.
209 * Never use TSD after the fork() since this is not only a different thread,
210 * it's a different process!
212 int Unx_fork_exec(tsd_t *TSD, environment *env, const char *cmdline, int *rcode)
214 static const char *interpreter[] = { "regina", /* preferable even if not */
215 /* dynamic */
216 "rexx" };
217 char **args ;
218 int i, rc, max_hdls = MaxFiles() ;
219 int broken_address_command = get_options_flag( TSD->currlevel, EXT_BROKEN_ADDRESS_COMMAND );
220 int subtype;
222 if ( ( rc = fork() ) != 0 )
223 return( rc );
225 /* Now we are the child */
227 #define STD_REDIR(hdl,dest) if ((hdl != -1) && (hdl != dest)) dup2(hdl, dest)
228 #define SET_MAXHDL(hdl) if (hdl > max_hdls) max_hdls = hdl
229 #define SET_MAXHDLS(ep) SET_MAXHDL(ep.hdls[0]); SET_MAXHDL(ep.hdls[1])
231 /* Force the standard redirections: */
232 STD_REDIR(env->input.hdls[0], 0);
233 STD_REDIR(env->output.hdls[1], 1);
234 if (env->error.SameAsOutput)
236 STD_REDIR(1, 2);
238 else
240 STD_REDIR(env->error.hdls[1], 2);
243 /* any handle greater than the default ? */
244 SET_MAXHDLS(env->input);
245 SET_MAXHDLS(env->output);
246 if (!env->error.SameAsOutput)
247 SET_MAXHDLS(env->error);
249 for (i=3; i <= max_hdls; i++)
250 close( i ) ;
253 * If the BROKEN_ADDRESS_COMMAND OPTION is in place,
254 * and our environment is COMMAND, change it to SYSTEM
256 if ( env->subtype == SUBENVIR_PATH /* was SUBENVIR_COMMAND */
257 && broken_address_command )
258 subtype = SUBENVIR_SYSTEM;
259 else
260 subtype = env->subtype;
262 switch ( subtype )
264 case SUBENVIR_PATH:
265 args = makeargs(cmdline, '\\');
266 execvp(*args, args);
267 break;
269 case SUBENVIR_COMMAND:
270 args = makeargs(cmdline, '\\');
271 execv(*args, args);
272 break;
274 case SUBENVIR_SYSTEM:
275 #if defined(HAVE_WIN32GUI)
276 rc = mysystem( cmdline ) ;
277 #else
278 rc = system( cmdline ) ;
279 #endif
280 #ifdef VMS
281 exit (rc); /* This is a separate process, exit() is allowed */
282 #else
283 if ( WIFEXITED( rc ) )
285 fflush( stdout );
286 _exit( (int) WEXITSTATUS(rc) ); /* This is a separate process, exit() is allowed */
288 else if ( WIFSIGNALED( rc ) )
289 raise( WTERMSIG( rc ) ); /* This is a separate process, raise() is allowed */
290 else
291 raise( WSTOPSIG( rc ) ); /* This is a separate process, raise() is allowed */
292 #endif
293 break;
294 case SUBENVIR_REXX:
296 char *new_cmdline;
297 char **run;
298 int i;
299 unsigned len;
301 if (argv0 == NULL)
302 len = 7; /* max("rexx", "regina") */
303 else
305 len = strlen(argv0);
306 if (len < 7)
307 len = 7; /* max("rexx", "regina") */
309 len += strlen(cmdline) + 2; /* Blank + term ASCII0 */
311 if ((new_cmdline = (char *)malloc(len)) == NULL)
312 raise( SIGKILL ); /* This is a separate process, raise() is allowed */
314 if (argv0 != NULL) /* always the best choice */
316 strcpy(new_cmdline, argv0);
317 strcat(new_cmdline, " ");
318 strcat(new_cmdline, cmdline);
319 args = makeargs(new_cmdline, '\\');
320 execv(*args, args);
321 destroyargs(args);
324 /* load an interpreter by name from the path */
325 for (i = 0; i < (int) (sizeof(interpreter) / sizeof(interpreter[0]));i++)
327 strcpy(new_cmdline, interpreter[i]);
328 strcat(new_cmdline, " ");
329 strcat(new_cmdline, cmdline);
330 args = makeargs(new_cmdline, '\\');
331 execvp(*args, args);
332 destroyargs(args);
335 /* last chance, worst choice, use the re-entering code: */
336 strcpy(new_cmdline, "\"\" ");
337 strcat(new_cmdline, cmdline);
338 args = makeargs(new_cmdline, '\\');
340 for (i = 0, run = args; *run; run++)
341 i++;
342 fflush( stdout );
343 _exit(__regina_reexecute_main(i, args));
346 default: /* illegal subtype */
347 raise( SIGKILL ) ; /* This is a separate process, raise() is allowed */
350 /* exec() failed */
351 raise( SIGKILL ); /* This is a separate process, raise() is allowed */
352 #undef SET_MAXHDLS
353 #undef SET_MAXHDL
354 #undef STD_REDIR
355 return -1; /* keep the compiler happy */
358 /* wait waits for a process started by fork_exec.
359 * In general, this is called after the complete IO to the called process but
360 * it isn't guaranteed. Never call if you don't expect a sudden death of the
361 * subprocess.
362 * Returns the exit status of the subprocess under normal circumstances. If
363 * the subprocess has died by a signal, the return value is -(100+signalnumber)
365 static int Unx_wait(int process)
367 int rc, retval, status;
368 #ifdef VMS
369 for ( ; ; )
371 rc = wait( &status ) ;
372 if (rc != -1)
373 break;
374 rc = errno;
375 if (rc == EINTR)
376 continue;
377 break;
379 retval = status & 0xff ;
380 #else
381 for ( ; ; )
383 # ifdef NEXT
385 * According to Paul F. Kunz, NeXTSTEP 3.1 Prerelease doesn't have
386 * the waitpid() function, so wait() must be used instead. The
387 * following klugde will remain until NeXTSTEP gets waitpid().
389 wait( &status ) ;
390 # else /* ndef NEXT */
391 # ifdef DOS
392 rc = wait( &status ) ;
393 # else /* ndef DOS */
394 rc = waitpid( process, &status, 0 ) ;
395 # endif /* def DOS */
396 # endif /* def NEXT */
397 if (rc != -1)
398 break;
399 rc = errno;
400 if (rc == EINTR)
401 continue;
402 break;
404 /* still ndef VMS */
405 if (WIFEXITED(status))
407 retval = (int) WEXITSTATUS(status);
408 if ( retval < 0 )
409 retval = -retval;
411 else if (WIFSIGNALED(status))
413 retval = -WTERMSIG(status);
414 if ( retval > 0 )
415 retval = -retval;
416 else if ( retval == 0 )
417 retval = -1;
419 else
421 retval = -WSTOPSIG(status);
422 if ( retval > 0 )
423 retval = -retval;
424 else if ( retval == 0 )
425 retval = -1;
427 #endif /* def VMS */
428 return(retval);
431 /* open_subprocess_connection acts like the unix-known function pipe and sets
432 * ep->RedirectedFile if necessary. Just in the latter case ep->data
433 * is set to the filename.
435 static int Unx_open_subprocess_connection(const tsd_t *TSD, environpart *ep)
437 return(pipe(ep->hdls));
440 /* sets the given handle to the non-blocking mode. The value may change.
441 * async_info CAN be used to add the handle to the internal list of watched
442 * handles.
444 static void Unx_unblock_handle( int *handle, void *async_info )
446 int fl ;
448 if (*handle == -1)
449 return ;
451 fl = fcntl( *handle, F_GETFL ) ;
453 if ( fl == -1 ) /* We can either abort or try to continue, try to */
454 return ; /* continue for now. */
456 fcntl( *handle, F_SETFL, fl | O_NONBLOCK ) ;
459 /* restart_file sets the file pointer of the given handle to the beginning.
461 static void Unx_restart_file(int hdl)
463 lseek(hdl, 0l, SEEK_SET);
466 /* close acts like close() but closes a handle returned by
467 * open_subprocess_connection.
468 * async_info MAY be used to delete the handle from the internal list of
469 * watched handles.
471 static int Unx_close(int handle, void *async_info)
473 return(close(handle));
477 * close_special acts like close() but closes any OS specific handle.
478 * The close happens if the handle is not -1. A specific operation may be
479 * associated with this. Have a look for occurances of "hdls[2]".
481 static void Unx_close_special( int handle )
483 assert( handle == -1 );
486 /* read acts like read() but returns either an error (return code
487 * == -errno) or the number of bytes read. EINTR and friends leads to a
488 * re-read.
489 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
490 * we shall use blocked IO here.
492 static int Unx_read(int hdl, void *buf, unsigned size, void *async_info)
494 int done ;
496 do {
497 done = read( hdl, buf, size ) ;
498 } while ((done == -1) && (errno == EINTR));
500 if (done < 0)
502 done = errno;
503 if (done == 0) /* no error set? */
504 done = EPIPE ; /* good assumption */
505 #if defined(EWOULDBLOCK) && defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
506 /* BSD knows this value with the same meaning as EAGAIN */
507 if (done == EWOULDBLOCK)
508 done = EAGAIN ;
509 #endif
510 return( -done ) ; /* error */
513 return(done);
516 /* write acts like write() but returns either an error (return code
517 * == -errno) or the number of bytes written. EINTR and friends leads to a
518 * re-write.
519 * use_blocked_io is a flag. If set, the handle is set to blocked IO and
520 * we shall use blocked IO here.
521 * The file must be flushed if buf or size are 0.
523 static int Unx_write(int hdl, const void *buf, unsigned size, void *async_info)
525 int done ;
527 if ((buf == NULL) || (size == 0)) /* nothing to to for flushing buffers */
528 return(0);
530 do {
531 done = write( hdl, buf, size ) ;
532 } while ((done == -1) && (errno == EINTR));
534 if (done < 0)
536 done = errno;
537 if (done == 0) /* no error set? */
538 done = ENOSPC ; /* good assumption */
539 #if defined(EWOULDBLOCK) && defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
540 /* BSD knows this value with the same meaning as EAGAIN */
541 if (done == EWOULDBLOCK)
542 done = EAGAIN ;
543 #endif
544 return( -done ) ; /* error */
547 return(done);
550 /* create_async_info return an opaque structure to allow the process wait for
551 * asyncronous IO. There are three IO slots (in, out, error) which can be
552 * filled by add_waiter. The structure can be cleaned by reset_async_info.
553 * The structure must be destroyed by delete_async_info.
555 static void *Unx_create_async_info(const tsd_t *TSD)
557 AsyncInfo *ai = (AsyncInfo *)MallocTSD(sizeof(AsyncInfo));
559 ai->TSD = TSD;
560 return(ai);
562 /* delete_async_info deletes the structure created by create_async_info and
563 * all of its components.
565 static void Unx_delete_async_info(void *async_info)
567 AsyncInfo *ai = (AsyncInfo *)async_info;
569 if (ai == NULL)
570 return;
571 Free_TSD(ai->TSD, ai);
574 #if defined(POLLIN) && defined(HAVE_POLL) /* we have poll() */
576 /* reset_async_info clear async_info in such a way that fresh add_waiter()
577 * calls can be performed.
579 static void Unx_reset_async_info(void *async_info)
581 AsyncInfo *ai = (AsyncInfo *)async_info;
583 ai->_p_cnt = 0;
586 /* add_async_waiter adds a further handle to the asyncronous IO structure.
587 * add_as_read_handle must be != 0 if the next operation shall be a
588 * read, else it must be 0 for write.
589 * Call reset_async_info before a wait-cycle to different handles and use
590 * wait_async_info to wait for at least one IO-able handle.
592 static void Unx_add_async_waiter(void *async_info, int handle, int add_as_read_handle)
594 AsyncInfo *ai = (AsyncInfo *)async_info;
596 assert(ai->_p_cnt < 3);
597 ai->_p[ai->_p_cnt].fd = handle;
598 ai->_p[ai->_p_cnt++].events = (add_as_read_handle) ? POLLIN : POLLOUT;
601 /* wait_async_info waits for some handles to become ready. This function
602 * returns if at least one handle becomes ready.
603 * A handle can be added with add_async_waiter to the bundle of handles to
604 * wait for.
605 * No special handling is implemented if an asyncronous interrupt occurs.
606 * Thus, there is no guarantee to have handle which works.
608 static void Unx_wait_async_info(void *async_info)
610 AsyncInfo *ai = (AsyncInfo *)async_info;
612 if (ai->_p_cnt)
613 poll(ai->_p, ai->_p_cnt, -1);
616 #else /* end of POLLIN, must be select */
618 /* reset_async_info clear async_info in such a way that fresh add_waiter()
619 * calls can be performed.
621 static void Unx_reset_async_info(void *async_info)
623 AsyncInfo *ai = async_info;
625 FD_ZERO( &ai->_p_in );
626 FD_ZERO( &ai->_p_out );
627 ai->_p_max = -1 ;
630 /* add_async_waiter adds a further handle to the asyncronous IO structure.
631 * add_as_read_handle must be != 0 if the next operation shall be a
632 * read, else it must be 0 for write.
633 * Call reset_async_info before a wait-cycle to different handles and use
634 * wait_async_info to wait for at least one IO-able handle.
636 static void Unx_add_async_waiter(void *async_info, int handle, int add_as_read_handle)
638 AsyncInfo *ai = async_info;
640 FD_SET(handle,(add_as_read_handle) ? &ai->_p_in : &ai->_p_out ) ;
641 if (handle > ai->_p_max)
642 ai->_p_max = handle ;
645 /* wait_async_info waits for some handles to become ready. This function
646 * returns if at least one handle becomes ready.
647 * A handle can be added with add_async_waiter to the bundle of handles to
648 * wait for.
649 * No special handling is implemented if an asyncronous interrupt occurs.
650 * Thus, there is no guarantee to have handle which works.
652 static void Unx_wait_async_info(void *async_info)
654 AsyncInfo *ai = async_info;
656 if (ai->_p_max >= 0)
657 select( ai->_p_max+1, &ai->_p_in, &ai->_p_out, NULL, NULL);
660 #endif /* POLLIN or select */
663 static int Unx_uname(struct regina_utsname *name)
665 struct utsname osdata;
668 * Don't know whether utsname uses pointer or char[].
669 * So just copy data.
671 if ( uname( &osdata ) < 0 )
673 memset( name, 0, sizeof(struct regina_utsname) );
674 return -1;
677 strcpy( name->sysname, osdata.sysname );
678 strcpy( name->nodename, osdata.nodename );
679 strcpy( name->release, osdata.release );
680 strcpy( name->version, osdata.version );
681 strcpy( name->machine, osdata.machine );
683 return 0;
686 static void Unx_init(void)
690 OS_Dep_funcs __regina_OS_Unx =
692 Unx_init, /* init */
693 Unx_setenv, /* setenv */
694 Unx_fork_exec, /* fork_exec */
695 Unx_wait, /* wait */
696 Unx_open_subprocess_connection, /* open_subprocess_connection */
697 Unx_unblock_handle, /* unblock_handle */
698 Unx_restart_file, /* restart_file */
699 Unx_close, /* close */
700 Unx_close_special, /* close_special */
701 Unx_read, /* read */
702 Unx_write, /* write */
703 Unx_create_async_info, /* create_async_info */
704 Unx_delete_async_info, /* delete_async_info */
705 Unx_reset_async_info, /* reset_async_info */
706 Unx_add_async_waiter, /* add_async_waiter */
707 Unx_wait_async_info, /* wait_async_info */
708 Unx_uname /* uname */