/* * Pure Data Packet header file. Words for pipe and file manipulation. * Copyright (c) by Tom Schouten * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* maybe there's no point in having this file, since there's unixy stuff all over the place. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include // Darwin #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #endif #ifndef RUSAGE_CHILDREN #define RUSAGE_CHILDREN -1 #endif #include #include #include #include #include #include #include #include #include #include // protocol header size #define HEADER_SIZE 256 // FIXME: check atomicity // since interrupts are process-local, it doesn't make sense to store // flags in the VM. just have the VM collect it if necessary. // signal handlers static void signal_ignore(int n) { // pf_post("igoring signal %d", n); } // fatal errors call the cleanup method, then exit static void signal_fatal(int n){ switch(n){ case 1: pf_exit_program(0); default: fprintf(stderr, "Signal %d: %s\n", n, strsignal(n)); pf_exit_program(1); } } static void install_signal_handler(int signal, void (*handler)(int)) { struct sigaction sa; sa.sa_flags = 0; sa.sa_handler = handler; memset(&sa.sa_mask, 0, sizeof(sa.sa_mask)); //sa.sa_restorer = 0; if (sigaction(signal, &sa, 0) < 0){ PF_ASSERT(0); } } static void setup_signals(void){ install_signal_handler(SIGPIPE, signal_ignore); install_signal_handler(SIGINT, signal_fatal); install_signal_handler(SIGFPE, signal_fatal); // install_signal_handler(SIGSEGV, signal_fatal); // better to let crash? install_signal_handler(SIGHUP, signal_fatal); } // just a simple subshell command ('system') // use the stream inferior process stuff for more elaborate things static PF_PRIMITIVE(system_command) { char *command; pid_t pid; int retval = 0; CHECK1(a_packet); command = STRING(ARG0); if (!command) return e_type; pid = fork(); /* child: execute command in subshell */ if (!pid) { execl("/bin/sh", "/bin/sh", "-c", command, NULL); exit(1); // fallthrough } /* parent: wait for child to terminate */ else { waitpid(pid, &retval, 0); } DROP1; PUSH_INT(retval); EXIT; } /* exit program. TODO: fix cleanup */ static PF_PRIMITIVE(pf_exit_now) { CHECK1(a_int); int exitcode = pf_list_pop(s).w_int; //pf_vm_cleanup(vm); //pf_dealloc(vm); // necessary ?? exit(exitcode); // not reached return e_internal; } // this used default mode 0777 static PF_PRIMITIVE(pf_mkdir){ CHECK1(a_packet); char *dir = STRING(ARG0); if (!dir) return e_type; if (-1 == mkdir(dir, 0777)){ THROW(e_file, "can't create directory %s", dir); } DROP1; EXIT; } static PF_PRIMITIVE(pf_chdir){ CHECK1(a_packet); char *dir = STRING(ARG0); if (!dir) return e_type; if (-1 == chdir(dir)){ THROW(e_file, "can't cd to directory %s", dir); } DROP1; EXIT; } /* check if file exists */ static PF_PRIMITIVE(pf_exists){ CHECK1(a_packet); char *name = STRING(ARG0); if (!name) return e_type; struct stat buf; int retval; if (-1 == stat(name, &buf)) { retval = 0; goto done; } retval = -1; done: DROP1; PUSH_INT(retval); EXIT; } static PF_PRIMITIVE(pf_getpid){ PUSH_INT(getpid()); EXIT; } static PF_PRIMITIVE(pf_rm){ CHECKN(1); char *name = STRING(ARG0); if (remove(name) < 0){ THROW(e_file, "can't remove %s : %s", name, strerror(errno)); } DROP; EXIT; } // fork daemon, run console in the original process static PF_PRIMITIVE(pf_fork_console){ CHECKN(1); char *socket = STRING(ARG0); int pid; char daemon_pid[10]; char *argv[]={"pf-console", socket, daemon_pid, 0}; if (pid = fork()){ sprintf(daemon_pid, "%d", pid); int fd = 2; while (close(++fd) == 0){ // pf_post("closed %d", fd); } // parent if (-1 == execvp(argv[0], argv)){ pf_post("can't execute %s : %s", argv[0], strerror(errno)); } exit(1); return e_internal; // not reached } else { // child (daemon) DROP; EXIT; } } /* SETUP CODE */ #define REGISTER_PRIMITIVE PF_REGISTER_FUNCTION PF_PRIMITIVE(pf_forth_unix_setup) { setup_signals(); REGISTER_PRIMITIVE(system_command, "system", "( string -- exitcode )\t" "Execute command string using OS shell."); REGISTER_PRIMITIVE(pf_exit_now, "pf-exit-now", "( exitcode -- )\t" "exit immediate, with error code."); REGISTER_PRIMITIVE(pf_fork_console, "pf-fork-console", "( unix.socket -- )\tFork off a daemon, and exec pf-console, attached to current terminal."); REGISTER_PRIMITIVE(pf_getpid, "getpid", "( -- int )\tGet process ID."); REGISTER_PRIMITIVE(pf_rm, "rmfile", "( path -- )\tRemove file."); REGISTER_PRIMITIVE(pf_mkdir, "mkdir", "( path -- )\tCreate directory."); REGISTER_PRIMITIVE(pf_chdir, "chdir", "( path -- )\tChange directory."); REGISTER_PRIMITIVE(pf_exists, "exists?", "( filename -- ?)\tCheck if file exists."); EXIT; } // no unix //void pf_forth_pipe(void) //{ // pf_post("WARNING: unix words not implemented."); //} //#endif