/* Uniflex Kermit's support task. This privileged task accounts for the following functions: 1. Set the baudrate of any terminal with that possibility. 2. Get the number of free blocks from the current device. 3. Set the specified date on a specified file. 4. Set the default directory (to keep track with the caller). 5. Send a break to the specified device. This program reads stdin (normally a pipe) to get the function. Current functions are: 1. 'b' - set the baudrate on the specified device 2. 'd' - set the specified date on the specified file 3. 'f' - get number of free blocks on the specified device 4. 's' - set the default directory 5. 'w' - send 'break' to the specified device 6. 'e' - exit the support task */ #asm info UniFLEX Kermit's support task info Author: Jur van der Burg info Nettelhorst 56 info 2402 LS Alphen aan den Rijn info The Netherlands info Version: V 1.2 #endasm #define PROTOCOL_VERSION 2 #include #include #include #include #include #include #define chtim touch /* other name in my library */ #define TRUE 1 #define FALSE 0 #define IN 0 #define OUT 1 #define BAUD 0 #define BREAK 1 #define ERROR (-1) #define TIMBYTE 0x2b main() { char command, mask, errmsg[128], device[30], file[128]; int status, len, owner, perms; unsigned int baudrate; struct stat buf; long get_freedisk(), free_blks, date; fstat(0,&buf); /* check standard input */ if (buf.st_nlink != 0) /* input must come from a pipe */ exit(0); /* bye bye, leave him in confusion */ signal(SIGTERM,SIG_IGN); /* Ignore this signals */ signal(SIGHUP,SIG_IGN); signal(SIGQUIT,SIG_IGN); signal(SIGINT,SIG_IGN); command = PROTOCOL_VERSION; write(OUT,&command,1); /* Send acknowledge to parent */ while (TRUE) /* Keep looking for commands */ { if (read(IN,&command,1) <= 0) /* Get command byte */ exit(1); /* Read error */ switch(command) { case 'b': /* Set device baudrate */ read(IN,&len,2); /* Get device name length */ read(IN,device,len); /* Get device name */ read(IN,&baudrate,2); /* Get the baudrate */ read(IN,&mask,1); /* Get the mask */ status = set_dev(device,baudrate,mask,errmsg,BAUD); /* Set */ write(OUT,&status,2); /* Return status */ if (status) send_error(errmsg); /* Send error message */ break; case 'w': /* Send break to device */ read(IN,&len,2); /* Get device name length */ read(IN,device,len); /* Get device name */ status = set_dev(device,0,0,errmsg,BREAK); /* Send it */ write(OUT,&status,2); /* Return status */ if (status) send_error(errmsg); /* Send error message */ break; case 'd': read(IN,&len,2); /* Get file name length */ read(IN,file,len); /* Get file name */ read(IN,&date,4); /* Get the date */ status = set_date(file,date,errmsg); /* Set it */ write(OUT,&status,2); /* Return status */ if (status) send_error(errmsg); /* Send error message */ break; case 'f': read(IN,&len,2); /* Get device name length */ read(IN,device,len); /* Get device name */ free_blks = get_freedisk(device,errmsg);/* Get # of blocks */ write(OUT,&free_blks,4); /* Send to parent */ if (free_blks == (long) ERROR) send_error(errmsg); /* Send error message */ break; case 's': read(IN,&len,2); /* Get directory name length */ read(IN,device,len); /* Get directory name */ status = set_dir(device,errmsg); /* Set it */ write(OUT,&status,2); /* Return status */ if (status) send_error(errmsg); /* Send error message */ break; case 'e': exit(0); /* bye bye */ default: break; /* Ignore unknown command */ } } } send_error(msg) char *msg; { int length; length = strlen(msg) + 1; /* Must send terminator as well */ write(OUT,&length,2); /* Send length */ write(OUT,msg,length); /* Send message */ } /* Routine to set the baudrate of a port, or to send a break to it. The user must either be the owner or have read and write access to the selected device. A check will be made if the system consists of new hardware. In the case of old hardware the baudrate is not under program control. */ #define P_TTY 0x5c /* Pointer to tty structure */ #define P_NTTY 0x5008 /* Pointer to number of tty's */ int valid_speed[] = { /* Table containing valid speed's */ 75,0xff, 150,0xee, 300,0xdd, 600,0xcc, 1200,0xbb, 2400,0xaa, 4800,0x99, 9600,0x88, 0 }; struct ttydef { /* Internal UniFLEX tty structure definition */ char *rawq, *canq, *outq; unsigned int devadr; char flags, delay, major, minor, delcnt, col, kill, erase, speed, type, state, xstate }; #define ISOPEN 0x04 /* Current state */ jmp_buf env; set_dev(dev,speed,mask,errmsg,what) char *dev; unsigned int speed; char *errmsg; char mask, what; { int *fp, *fp1, *p, i, timeout(); char found, speed_val, n_tty; unsigned int c, np; struct stat buf; struct ttydef tt_dsc; struct ttydef *sp; if (stat(dev,&buf)) /* Get device parameters */ return(prterr("Invalid device specified",errmsg)); if ((buf.st_mode & S_IFMT) != S_IFCHR) /* Check for a character device */ return(prterr("Not a character device",errmsg)); buf.st_mode &= S_IPRM; /* Mask type bits */ if (!((buf.st_mode & S_IOREAD) && /* Check for read and write */ (buf.st_mode & S_IOWRITE))) /* access for others */ if (buf.st_uid != getuid()) /* No access, check if he owns */ /* the device */ return(prterr("Don't own device",errmsg)); if (what == BAUD) { found = FALSE; p = valid_speed; while (*p) { if (speed == *p++) /* Compare against table */ { speed_val = *p; /* Got it ! */ found = TRUE; break; } p++; /* Point to next entry */ } if (!found) return(prterr("Invalid speed specified",errmsg)); if (mask) speed_val &= 0x77; /* Mask RTS and DCD */ } /* The following piece of code ensures that a device address will be available in the internal data structures for the specified device. This is simply done by opening the device. In case it's not ready (modem for example) a timeout has been setup to allow the program to continue. */ signal(SIGALRM,timeout); /* Catch the timeout trap */ alarm(1); /* One second timeout */ if (!setjmp(env)) /* If for the first time */ fp = open(dev,2); /* Open the port */ alarm(0); /* Reset timer */ close(fp); if ((fp = open("/dev/smem",2)) == ERROR) /* Must open this one */ return(prterr("Error opening '/dev/smem'",errmsg)); readat(fp,P_NTTY,&np,2); /* Get pointer to number of tty's */ readat(fp,np,&n_tty,1); /* Get number of tty's */ readat(fp,P_TTY,&sp,2); /* Get pointer to TTY structure */ found = FALSE; for (i = 0; i < n_tty; i++) { readat(fp,sp++,&tt_dsc,sizeof(struct ttydef));/* Read total structure */ if (tt_dsc.devadr == 0) /* Skip on zero device address */ continue; if ((tt_dsc.major == ((int)(buf.st_size >> 16) & 0xff)) && (tt_dsc.minor == ((int)buf.st_size & 0xff))) /* Chk maj + min id */ { readat(fp,tt_dsc.devadr + 2,&c,2); /* Get speed register */ if ((c == 0xffff) || (what == BREAK)) /* Should read as 'ffff' */ found = TRUE; else return(prterr("Baudrate not programmable",errmsg)); break; } } if (found) { setuid(0); /* Make sure we've got the priv's */ if (what == BAUD) writeat(fp,tt_dsc.devadr + 2,&speed_val,1); /* Update speed */ else if (what == BREAK) poke_tty(fp,&tt_dsc,--sp); } else return(prterr("Unable to set baudrate",errmsg)); close(fp); return(NULL); } poke_tty(fp,tt_dsc,sp) int *fp; struct ttydef *tt_dsc, *sp; { char break_val, org_value, new_speed; org_value = (tt_dsc->speed & ~0x1c) | 0x81; /* Current characteristics */ if (!(tt_dsc->state & ISOPEN)) /* RTS high if port closed */ org_value |= 0x40; break_val = org_value | 0x60; /* Add 'break' bits */ new_speed = tt_dsc->speed | 0x60; writeat(fp,&sp->speed,&new_speed,1); /* break will not be terminated */ writeat(fp,tt_dsc->devadr,&break_val,1); /* if new char is received */ short_delay(fp,3); /* 300 Ms delay */ writeat(fp,&sp->speed,&tt_dsc->speed,1); writeat(fp,tt_dsc->devadr,&org_value,1); } readat(fp,address,value,count) int *fp; unsigned int address; char *value; int count; { lseek(fp, (unsigned long)address, 0); read (fp, value, count); } writeat(fp,address,value,count) int *fp; unsigned int address; char *value; int count; { lseek(fp, (unsigned long)address, 0); write(fp, value, count); } short_delay(fp,time) int *fp, time; { int i; char n, m; n = m = -1; for (i = 0; i < time + 1; i++) { while (n == m) { lseek(fp, (long)TIMBYTE, 0); /* Position to time byute */ read(fp,&m,1); /* Read current value */ } n = m; } } timeout() { longjmp(env,TRUE); /* Return after timeout */ } prterr(str,errstr) char *str, *errstr; { sprintf(errstr,"%s",str); return(ERROR); } /* This routine returns the number of free blocks on the specified device or directory. The real device is found by searching for it in the '/dev' directory, and comparing the major and minor id of the device with the device number of the specified directory. If the device name is found, a 'sync' is done to be sure that the information on the disk is current. Then the SIR is read and the number of free blocks is retrieved from that. */ long get_freedisk(indev,errstr) char *indev, *errstr; { struct stat statbuf; struct direct dirptr; int device, status, fpt; unsigned int mode; char found, filename[20]; long freeblks, get_disk(); fpt = ERROR; found = FALSE; /* Loop end indicator */ strcpy(filename,"/dev/"); if (!stat(indev,&statbuf)) /* Get stat of specified directory */ { device = statbuf.st_dev; /* Save device major and minor */ mode = statbuf.st_mode & S_IFMT; /* mask for file type */ if (mode == S_IFCHR) /* Character device not allowed */ return(prterr("Invalid device or file specified",errstr)); else if (mode == S_IFBLK) /* already a block device ? */ { strcpy(filename,indev); /* supply device name */ found = TRUE; } if ((fpt = open("/dev",0)) != ERROR) while (!found) /* Until we're done... */ { status = read(fpt,&dirptr,sizeof(struct direct)); /* Get entry */ if ((status == ERROR) || (status == NULL))/* Quit on err or EOF */ break; if (dirptr.d_ino) /* If not deleted */ { strcpy(&filename[5],dirptr.d_name); if (stat(filename,&statbuf)) /* Get status */ break; else { statbuf.st_mode &= S_IFMT; /* Mask file type bits */ if ((statbuf.st_mode == S_IFBLK) && /* block type device */ (statbuf.st_size == device)) /* Maj. and Min. match ? */ found = TRUE; /* Yes, return name */ } } } } else return(prterr("Invalid device or file specified",errstr)); if (fpt != ERROR) close(fpt); /* We're done with it */ if (found) if ((freeblks = get_disk(filename)) != ERROR) /* Get the data */ return(freeblks); return(prterr("Disk read error",errstr)); } long get_disk(name) char *name; { int dpt; unsigned char freeb[3]; long size; size = ERROR; /* assume error */ if ((dpt = open(name,0)) == ERROR) /* Open the block device */ return(ERROR); sync(); /* Make sure count is up to date */ if (lseek(dpt,(long) 512 + 21, 0) != ERROR) /* Position in SIR */ if (read(dpt,freeb,3) != ERROR) /* Get the three bytes */ l3tol(&size,freeb,1); /* Convert to long int */ close(dpt); return (size); } set_date(filename,filedate,errmsg) char *filename, *errmsg; long filedate; { if (chtim(filename,filedate) == ERROR) /* change the date */ return(prterr("Error setting file date",errmsg)); else return(NULL); } set_dir(directory,errmsg) char *directory, *errmsg; { if (chdir(directory) == ERROR) return(prterr("Error setting directory",errmsg)); else return NULL; }