#include #include #include #include #include #include #include #include #include #include "ixemul.h" #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG #define dprintf(fmt, args...) kprintf(fmt, ##args) #else #define dprintf(fmt, args...) #endif /* this is our custom packet, which passes along an ixemul-private file id, which we then use to clone that file into our file-table space. All later operations are then performed on file-descriptors as usual ;-)) */ #define ACTION_IXEMUL_MAGIC 0x4242 /* *very* magic ;-)) */ #define DOS_TRUE -1 #define DOS_FALSE 0 /* we require at least ixemul.library v39.41 */ #define NEEDED_IX_VERSION 39 /* or better */ #define NEEDED_IX_REVISION 41 /* or better */ int handler_mainloop (struct DeviceNode *dev_node, char **argv, int *errno); static int __errno_to_ioerr (int err); int ix_exec_entry (struct DeviceNode *argc, char **argv, int *environ, int *real_errno, int (*main)(struct DeviceNode *, char **, int *)); /* guarantee that the first location in the code hunk is a jump to where we start, and not some shared string that just happend to land at location 0... */ #ifdef __MORPHOS__ asm (".section \".text\"; b ENTRY;"); #else asm (".text; jmp _ENTRY;"); #endif static const char version_id[] = "\000$VER: ixpipe-handler 1.2 (8.2.2006)"; void *ixemulbase; struct ExecBase *SysBase; #ifdef __MORPHOS__ int __amigappc__ = 1; int (**_ixbasearray)(); #endif /* returnpkt() - packet support routine * here is the guy who sends the packet back to the sender... * * (I modeled this just like the BCPL routine [so its a little redundant] ) */ static void returnpkt(struct DosPacket *packet, struct Process *myproc, ULONG res1, ULONG res2) { struct Message *mess; struct MsgPort *replyport; packet->dp_Res1 = res1; packet->dp_Res2 = res2; replyport = packet->dp_Port; mess = packet->dp_Link; packet->dp_Port = &myproc->pr_MsgPort; mess->mn_Node.ln_Name = (char *) packet; #if 0 mess->mn_Node.ln_Succ = mess->mn_Node.ln_Pred = 0; #endif PutMsg (replyport, mess); } static void returnpktplain(struct DosPacket *packet, struct Process *myproc) { returnpkt(packet, myproc, packet->dp_Res1, packet->dp_Res2); } /* * taskwait() ... Waits for a message to arrive at your port and * extracts the packet address which is returned to you. */ static struct DosPacket *taskwait(struct Process *myproc) { struct MsgPort *myport; struct Message *mymess; myport = &myproc->pr_MsgPort; WaitPort (myport); mymess = GetMsg (myport); return ((struct DosPacket *)mymess->mn_Node.ln_Name); } int ENTRY (void) { struct ixemul_base *ixbase; struct Process *me; struct DosPacket *startup_packet; struct DeviceNode *dev_node; int errno; struct MsgPort port; struct WBStartup msg; char *argv[1]; SysBase = *(struct ExecBase **) 4; me = (struct Process *) SysBase->ThisTask; dprintf("ixp-$%lx: waiting for startup-packet\n", me); /* wait for the startup packet */ startup_packet = taskwait (me); dprintf("ixp-$%lx: got startup packet\n", me); /* pr_CLI is NULL since we are a handler, so ix_open will expect a wb message. Fake one. */ port.mp_Flags = PA_IGNORE; NEWLIST(&port.mp_MsgList); msg.sm_Message.mn_ReplyPort = &port; msg.sm_Process = &me->pr_MsgPort; msg.sm_NumArgs = 0; msg.sm_ToolWindow = NULL; msg.sm_ArgList = 0; PutMsg(&me->pr_MsgPort, &msg.sm_Message); ixbase = (APTR)OpenLibrary ("ixemul.library", NEEDED_IX_VERSION); if (ixbase) { if (ixbase->ix_lib.lib_Version == NEEDED_IX_VERSION && ixbase->ix_lib.lib_Revision < NEEDED_IX_REVISION) CloseLibrary (&ixbase->ix_lib); else { /* make the external library glue work */ ixemulbase = ixbase; #ifdef __MORPHOS__ _ixbasearray = ixbase->basearray; #endif dev_node = BTOCPTR (startup_packet->dp_Arg3); dev_node->dn_Task = &me->pr_MsgPort; returnpkt (startup_packet, me, DOS_TRUE, 0); dprintf ("ixp-$%lx: init ok, entering handler mainloop\n", me); /* ignore the result _exit() might pass to us. pass our device node as `argc' to handler_mainloop() */ /* put something meaningful in argv[], for is_ixconfig() */ argv[0] = "ixpipe"; ix_exec_entry (dev_node, argv, &errno, &errno, handler_mainloop); CloseLibrary (&ixbase->ix_lib); return 0; } } dprintf ("ixp-$%lx: init-error\n", me); returnpkt (startup_packet, me, DOS_FALSE, ERROR_BAD_STREAM_NAME); return 0; } void dummy_sighandler () { } jmp_buf jmpbuf; void panic_sighandler (int sig) { longjmp (jmpbuf, sig); } int handler_mainloop (struct DeviceNode *dev_node, char **argv, int *errno) { struct DosPacket *volatile dp = NULL; struct Process *me = (struct Process *)SysBase->ThisTask; struct MsgPort *our_mp = &me->pr_MsgPort; int i; for (i = 1; i < SIGMSG; i++) signal (i, panic_sighandler); /* disable ^C propagation as good as we can... */ signal (SIGMSG, dummy_sighandler); /* terminated by ACTION_END, Close() that is */ for (;;) { if ((i = setjmp (jmpbuf))) { dprintf ("ixp-$%lx: SIGNAL %ld\n", me, i); if (dp->dp_Type == ACTION_WRITE && i == SIGPIPE) { Signal (dp->dp_Port->mp_SigTask, SIGBREAKF_CTRL_C); returnpkt (dp, me, -1, 0); /* return EOF */ continue; } /* should look like `SIG' plus number ;-) */ returnpkt (dp, me, DOS_FALSE, 516000 + i); continue; } dprintf ("ixp-$%lx: Waiting for packet...\n", me); while (!(dp = taskwait (me))) ; /* find out what they want us to do.... */ switch (dp->dp_Type) { case ACTION_IXEMUL_MAGIC: { /* this is sort of an `Open', that is, we fill out a struct FileHandle. The reason I didn't chose to `abuse' the various ACTION_FIND{INPUT,OUTPUT} packets is simple: I want an ordinary Open() call to fail! The semantics are, that you pass a hex string describing the id as name. */ int fd; char name[255]; /* a BSTR can't address more ;-) */ u_char *cp; unsigned int id; struct FileHandle *fh; fh = BTOCPTR (dp->dp_Arg1); cp = BTOCPTR (dp->dp_Arg3); if (cp && fh) { bcopy (cp + 1, name, *cp); name[*cp] = 0; /* in case the device-qualifier is still contained in the name */ cp = index (name, ':'); if (cp) cp++; else cp = name; if (sscanf (cp, "%x", &id) == 1) { /* this fcntl() command does not require a valid descriptor. It's quite unique in this behavior... */ fd = fcntl (-1, F_INTERNALIZE, id); if (fd >= 0) { fh->fh_Arg1 = fd; fh->fh_Type = our_mp; fh->fh_Port = 0; /* we're not interactive, are we? */ dprintf ("ixp-$%lx: successful open, fd = %ld\n", me, fd); /* Setting the dn_Task field back to 0 makes each successive opening of IXPIPE: spawn a new handler. This is essential, or opening would block, if the handler is inside a read/write wait */ dev_node->dn_Task = 0; returnpkt (dp, me, DOS_TRUE, 0); break; } } } dprintf ("ixp-$%lx: open failed somehow.. \n", me); /* default is to return object-not-found.. */ returnpkt (dp, me, DOS_FALSE, ERROR_OBJECT_NOT_FOUND); break; } /* all the following packets operate on file descriptors obtained in ACTION_IXEMUL_MAGIC. */ case ACTION_READ: dprintf ("ixp-$%lx: read (%ld, $%lx, %ld)\n", me, dp->dp_Arg1, (char *) dp->dp_Arg2, dp->dp_Arg3); dp->dp_Res1 = read (dp->dp_Arg1, (char *) dp->dp_Arg2, dp->dp_Arg3); if (dp->dp_Res1 < 0) dp->dp_Res2 = __errno_to_ioerr (*errno); else dp->dp_Res2 = 0; returnpktplain (dp, me); break; case ACTION_WRITE: dprintf ("ixp-$%lx: write (%ld, $%lx, %ld)\n", me, dp->dp_Arg1, (char *) dp->dp_Arg2, dp->dp_Arg3); dp->dp_Res1 = write (dp->dp_Arg1, (char *) dp->dp_Arg2, dp->dp_Arg3); if (dp->dp_Res1 < 0) dp->dp_Res2 = __errno_to_ioerr (*errno); else dp->dp_Res2 = 0; returnpktplain (dp, me); break; case ACTION_SEEK: dprintf ("ixp-$%lx: lseek (%ld, %ld, %ld)\n", me, dp->dp_Arg1, (char *) dp->dp_Arg2, dp->dp_Arg3); /* we have to return the previous offset, contrary to Unix which returns the offset after seek operation */ dp->dp_Res1 = lseek (dp->dp_Arg1, 0, SEEK_CUR); if (dp->dp_Res1 >= 0) { lseek (dp->dp_Arg1, dp->dp_Arg2, dp->dp_Arg3 + 1); dp->dp_Res2 = 0; } else dp->dp_Res2 = __errno_to_ioerr (*errno); returnpktplain (dp, me); break; /* a little present for the growing number of >1.3 users out there */ case ACTION_EXAMINE_FH: { struct FileInfoBlock *fib = BTOCPTR (dp->dp_Arg2); struct stat stb; long time; dprintf ("ixp-$%lx: fstat (%ld, )\n", me, dp->dp_Arg1); dp->dp_Res1 = fstat (dp->dp_Arg1, &stb) == 0 ? DOS_TRUE : DOS_FALSE; dp->dp_Res2 = (dp->dp_Res1 == DOS_FALSE) ? __errno_to_ioerr (*errno) : 0; if (dp->dp_Res1 == DOS_TRUE) { fib->fib_DiskKey = stb.st_ino; /* on the packet level, fib's contain the name as a BSTR */ strcpy (fib->fib_FileName + 1, "you won't be able to reopen me anyway"); fib->fib_FileName[0] = strlen (fib->fib_FileName + 1); fib->fib_Protection = stb.st_amode; /* nice we kept it ;-)) */ fib->fib_Size = stb.st_size; fib->fib_NumBlocks = stb.st_blocks; time = stb.st_mtime - (8*365+2)*24*3600; /* offset to unix-timesystem */ fib->fib_Date.ds_Tick = (time % 60) * TICKS_PER_SECOND; time /= 60; /* minutes per day, not minutes per hour! */ fib->fib_Date.ds_Minute = time % (60 * 24); time /= 60 * 24; fib->fib_Date.ds_Days = time; fib->fib_Comment[0] = 0; /* reserved stuff should normally be zero'd, so do right this */ bzero (fib->fib_Reserved, sizeof (fib->fib_Reserved)); /* this is a bit tricky ;-)) Wondering what AmigaOS programs might do when they're faced with a directory type when examining a filehandle.... */ if (S_ISDIR (stb.st_mode)) fib->fib_DirEntryType = ST_USERDIR; else if (S_ISCHR (stb.st_mode)) fib->fib_DirEntryType = ST_PIPEFILE; else if (S_ISLNK (stb.st_mode)) fib->fib_DirEntryType = ST_SOFTLINK; else fib->fib_DirEntryType = ST_FILE; fib->fib_EntryType = fib->fib_DirEntryType; } returnpktplain (dp, me); break; } case ACTION_IS_FILESYSTEM: dprintf ("ixp-$%lx: ACTION_IS_FILESYSTEM, return DOS_FALSE\n", me); returnpkt (dp, me, DOS_FALSE, 0); break; case ACTION_END: dprintf ("ixp-$%lx: close (%ld)\n", me, dp->dp_Arg1); close (dp->dp_Arg1); returnpkt (dp, me, DOS_TRUE, 0); /* terminates the handler */ Forbid (); return 0; case ACTION_READ_LINK: /* Yes, correct failure return is -1 indeed */ dprintf ("ixp-$%lx: ACTION_READ_LINK, return -1\n", me); returnpkt (dp, me, -1, ERROR_ACTION_NOT_KNOWN); break; default: dprintf ("ixp-$%lx: returning unknown packet %ld\n", me, dp->dp_Type); returnpkt (dp, me, DOS_FALSE, ERROR_ACTION_NOT_KNOWN); break; } } } static int __errno_to_ioerr (int err) { switch (err) { case EAGAIN: return ERROR_TASK_TABLE_FULL; case ENOMEM: return ERROR_NO_FREE_STORE; case E2BIG: return ERROR_LINE_TOO_LONG; case ENOEXEC: return ERROR_FILE_NOT_OBJECT; case EEXIST: return ERROR_OBJECT_EXISTS; case ENOENT: return ERROR_OBJECT_NOT_FOUND; default: case ENODEV: case EIO: return ERROR_ACTION_NOT_KNOWN; case EINVAL: return ERROR_OBJECT_WRONG_TYPE; case EROFS: return ERROR_DISK_WRITE_PROTECTED; case EXDEV: return ERROR_RENAME_ACROSS_DEVICES; case ENOTEMPTY: return ERROR_DIRECTORY_NOT_EMPTY; case ELOOP: return ERROR_TOO_MANY_LEVELS; case ENXIO: return ERROR_DEVICE_NOT_MOUNTED; case ESPIPE: return ERROR_SEEK_ERROR; case ENAMETOOLONG: return ERROR_COMMENT_TOO_BIG; case ENOSPC: return ERROR_DISK_FULL; case EACCES: return ERROR_READ_PROTECTED; /* could as well be one of the others... */ } }