/**************************************************************************** * This module is all original code * by Rob Nation * Copyright 1993, Robert Nation * You may use this code for any purpose, as long as the original * copyright remains in the source code and all documentation ****************************************************************************/ /*********************************************************************** * * code for launching afterstep modules. * ***********************************************************************/ #include "../configure.h" #include #include #include #include #include #include #include #include #include #include "afterstep.h" #include "menus.h" #include "misc.h" #include "parse.h" #include "screen.h" #include "module.h" int npipes; int *readPipes; int *writePipes; int *pipeOn; unsigned long *PipeMask; struct queue_buff_struct **pipeQueue; AFTER_INLINE int PositiveWrite(int module, unsigned long *ptr, int size); void DeleteQueueBuff(int module); void AddToQueue(int module, unsigned long *ptr, int size, int done); void initModules(void) { int i; npipes = GetFdWidth(); writePipes = (int *)safemalloc(sizeof(int)*npipes); readPipes = (int *)safemalloc(sizeof(int)*npipes); pipeOn = (int *)safemalloc(sizeof(int)*npipes); PipeMask = (unsigned long *)safemalloc(sizeof(unsigned long)*npipes); pipeQueue=(struct queue_buff_struct **) safemalloc(sizeof(struct queue_buff_struct *)*npipes); for(i=0;i0) { close(writePipes[i]); close(readPipes[i]); } while(pipeQueue[i] != NULL) { DeleteQueueBuff(i); } } } void executeModule(char *action,FILE *fd, char **win, int *context) { int afterstep_to_app[2],app_to_afterstep[2]; int i,val; char command[256]; char *cptr; char *aptr; char *args[10]; char *arg1 = NULL; char arg2[40]; char arg3[40]; char arg5[40]; char arg6[40]; char *end; extern char *ModulePath; extern FILE *config_fd; extern char *afterstep_file; if(action == NULL) return; strcpy(command,action); cptr = command; while((isspace(*cptr))&&(*cptr != '\n')&&(*cptr != 0)) cptr++; end = cptr; while((!(isspace(*end))&&(*end != '\n'))&&(*end != 0)&&(end <(command+256))) end++; if((*end == 0)||(end >= command+256)) aptr = NULL; else aptr = end+1; *end = 0; if(aptr) { while((isspace(*aptr)||(*aptr=='\n'))&&(*aptr!=0)&&(aptr<(command+256))) aptr++; if((*aptr == 0)||(*aptr == '\n')) aptr = NULL; } arg1 = findIconFile(cptr,ModulePath,X_OK); if(arg1 == NULL) { fprintf(stderr,"AfterStep: No such module %s %s\n",ModulePath,cptr); return; } /* Look for an available pipe slot */ i=0; while((i=0)) i++; if(i>=npipes) { fprintf(stderr,"afterstep: Too many Accessories!\n"); return; } /* I want one-ended pipes, so I open two two-ended pipes, * and close one end of each. I need one ended pipes so that * I can detect when the module crashes/malfunctions */ if(pipe(afterstep_to_app)!=0) { fprintf(stderr,"AfterStep: Failed to open pipe\n"); return; } if(pipe(app_to_afterstep)!=0) { fprintf(stderr,"AfterStep: Failed to open pipe2\n"); close(afterstep_to_app[0]); close(afterstep_to_app[1]); return; } val = fork(); if(val > 0) { /* This fork remains running afterstep */ /* close appropriate descriptors from each pipe so * that afterstep will be able to tell when the app dies */ close(app_to_afterstep[1]); close(afterstep_to_app[0]); /* add these pipes to afterstep's active pipe list */ writePipes[i] = afterstep_to_app[1]; readPipes[i] = app_to_afterstep[0]; pipeOn[i] = -1; PipeMask[i] = MAX_MASK; free(arg1); pipeQueue[i] = NULL; /* make the PositiveWrite pipe non-blocking. Don't want to jam up afterstep because of an uncooperative module */ fcntl(writePipes[i],F_SETFL,O_NDELAY); /* Mark the pipes close-on exec so other programs * won`t inherit them */ if (fcntl(readPipes[i], F_SETFD, 1) == -1) afterstep_err("module close-on-exec failed",NULL,NULL,NULL); if (fcntl(writePipes[i], F_SETFD, 1) == -1) afterstep_err("module close-on-exec failed",NULL,NULL,NULL); } else if (val ==0) { /* this is the child */ /* need to close config_fd if its still open! */ if(config_fd != (FILE *)NULL) /* Fixes some funny stuff with svr4 and stream IO */ /* fclose(config_fd) */ close(fileno(config_fd)); /* this fork execs the module */ close(afterstep_to_app[1]); close(app_to_afterstep[0]); sprintf(arg2,"%d",app_to_afterstep[1]); sprintf(arg3,"%d",afterstep_to_app[0]); sprintf(arg5,"%lx",(unsigned long)win); sprintf(arg6,"%lx",(unsigned long)context); args[0]=arg1; args[1]=arg2; args[2]=arg3; args[3]=afterstep_file; args[4]=arg5; args[5]=arg6; if(aptr != NULL) { args[6] = aptr; args[7] = 0; } else args[6]= 0; execvp(arg1,args); fprintf(stderr,"Execution of module failed: %s",arg1); perror(""); close(app_to_afterstep[1]); close(afterstep_to_app[0]); exit(1); } else { fprintf(stderr,"Fork failed\n"); free(arg1); } return; } int HandleModuleInput(Window w, int channel) { char text[256]; int size; int cont,n; char *newaction = NULL; /* Already read a (possibly NULL) window id from the pipe, * Now read an afterstep bultin command line */ n = read(readPipes[channel], &size, sizeof(int)); if(n < sizeof(int)) { KillModule(channel,1); return; } if(size >255) { fprintf(stderr,"Module command is too big (%d)\n",size); size=255; } pipeOn[channel] = 1; n = read(readPipes[channel],text, size); if(n < size) { KillModule(channel,2); return; } text[n]=0; n = read(readPipes[channel],&cont, sizeof(int)); if(n < sizeof(int)) { KillModule(channel,3); return; } if(cont == 0) { KillModule(channel,4); } if(strlen(text)>0) { char function[256],*ptr; MenuRoot *mr=0; char *item=NULL; extern int func_val_1,func_val_2,func,Context; extern struct config func_config[]; extern unsigned PopupCount; extern MenuRoot *PopupTable[MAXPOPUPS]; ASWindow *tmp_win; extern char *orig_tline; int n,unit_val_1,unit_val_2; char unit_1, unit_2; orig_tline = text; Event.xany.type = ButtonRelease; Event.xany.window = w; func_val_1 = 0; func_val_2 = 0; unit_1 = 's'; unit_2 = 's'; n = sscanf(text,"%s %d %d",function,&func_val_1,&func_val_2); if(n != 3) n = sscanf(text,"%s %d%c %d%c",function,&func_val_1,&unit_1,&func_val_2,&unit_2); if(mystrcasecmp(function,"SET_MASK")==0) { PipeMask[channel] = func_val_1; return; } if(mystrcasecmp(function,"UNLOCK")==0) { return 66; } func = F_NOP; match_string(func_config,function,"bad module function:",NULL); if((func == F_POPUP)||(func == F_FUNCTION)) { unsigned i; ptr = stripcpy2(text,0,True); if(ptr != NULL) for (i = 0; i < PopupCount; i++) if (mystrcasecmp(PopupTable[i]->name,ptr) == 0) { mr = PopupTable[i]; break; } if (!mr) { no_popup(ptr); func = F_NOP; } } else if((func == F_EXEC)||(func == F_RESTART)|| (func == F_CIRCULATE_UP)||(func == F_CIRCULATE_DOWN)|| (func == F_WARP)||(func == F_MODULE)) { if((func == F_EXEC)||(func == F_RESTART)||(func== F_MODULE)) { item = stripcpy2(text,0,True); newaction = stripcpy3(text,True); } else { item = stripcpy2(text,0,False); newaction = stripcpy3(text,False); } } if (XFindContext (dpy, w, ASContext, (caddr_t *) &tmp_win) == XCNOENT) { tmp_win = NULL; w = None; } if(tmp_win) { Event.xbutton.button = 1; Event.xbutton.x_root = tmp_win->frame_x; Event.xbutton.y_root = tmp_win->frame_y; Event.xbutton.x = 0; Event.xbutton.y = 0; Event.xbutton.subwindow = None; } else { Event.xbutton.button = 1; Event.xbutton.x_root = 0; Event.xbutton.y_root = 0; Event.xbutton.x = 0; Event.xbutton.y = 0; Event.xbutton.subwindow = None; } if(unit_1 == 'p') unit_val_1 = 100; else unit_val_1 = Scr.MyDisplayWidth; if(unit_2 == 'p') unit_val_2 = 100; else unit_val_2 = Scr.MyDisplayHeight; Context = GetContext(tmp_win,&Event,&w); ExecuteFunction(func,newaction, w, tmp_win, &Event, Context, func_val_1,func_val_2,unit_val_1,unit_val_2,mr,channel); } return; } void DeadPipe(int nonsense) { signal(SIGPIPE, DeadPipe); } void KillModule(int channel, int place) { close(readPipes[channel]); close(writePipes[channel]); readPipes[channel] = -1; writePipes[channel] = -1; pipeOn[channel] = -1; while(pipeQueue[channel] != NULL) { DeleteQueueBuff(channel); } return; } void Broadcast(unsigned long event_type, unsigned long num_datum, unsigned long data1, unsigned long data2, unsigned long data3, unsigned long data4, unsigned long data5, unsigned long data6, unsigned long data7) { int i; unsigned long body[10]; body[0] = START_FLAG; body[1] = event_type; body[2] = num_datum+3; if(num_datum>0) body[3] = data1; if(num_datum>1) body[4] = data2; if(num_datum>2) body[5] = data3; if(num_datum>3) body[6] = data4; if(num_datum>4) body[7] = data5; if(num_datum>5) body[8] = data6; if(num_datum>6) body[9] = data7; for(i=0;i0) body[3] = data1; if(num_datum>1) body[4] = data2; if(num_datum>2) body[5] = data3; if(num_datum>3) body[6] = data4; if(num_datum>4) body[7] = data5; if(num_datum>5) body[8] = data6; if(num_datum>6) body[9] = data7; PositiveWrite(module,body,(num_datum+3)*sizeof(unsigned long)); } void SendConfig(int module, unsigned long event_type, ASWindow *t) { unsigned long body[MAX_BODY_SIZE+HEADER_SIZE]; body[0] = START_FLAG; body[1] = event_type; body[2] = 27; body[3] = t->w; body[4] = t->frame; body[5] = (unsigned long)t; body[6] = t->frame_x; body[7] = t->frame_y; body[8] = t->frame_width; body[9] = t->frame_height; body[10] = t->Desk; body[11] = t->flags; body[12] = t->title_height; body[13] = t->boundary_width; body[14] = t->hints.base_width; body[15] = t->hints.base_height; body[16] = t->hints.width_inc; body[17] = t->hints.height_inc; body[18] = t->hints.min_width; body[19] = t->hints.min_height; body[20] = t->hints.max_width; body[21] = t->hints.max_height; body[22] = 0; body[23] = t->icon_pixmap_w; body[24] = t->hints.win_gravity; body[25] = t->TextPixel; body[26] = t->BackPixel; PositiveWrite(module,body,27*sizeof(unsigned long)); } void BroadcastConfig(unsigned long event_type, ASWindow *t) { unsigned long body[MAX_BODY_SIZE+HEADER_SIZE]; int i; body[0] = START_FLAG; body[1] = event_type; body[2] = 27; body[3] = t->w; body[4] = t->frame; body[5] = (unsigned long)t; body[6] = t->frame_x; body[7] = t->frame_y; body[8] = t->frame_width; body[9] = t->frame_height; body[10] = t->Desk; body[11] = t->flags; body[12] = t->title_height; body[13] = t->boundary_width; body[14] = t->hints.base_width; body[15] = t->hints.base_height; body[16] = t->hints.width_inc; body[17] = t->hints.height_inc; body[18] = t->hints.min_width; body[19] = t->hints.min_height; body[20] = t->hints.max_width; body[21] = t->hints.max_height; body[22] = 0; body[23] = t->icon_pixmap_w; body[24] = t->hints.win_gravity; body[25] = t->TextPixel; body[26] = t->BackPixel; for(i=0;i AFTER_INLINE int PositiveWrite(int module, unsigned long *ptr, int size) { if((pipeOn[module]<0)||(!((PipeMask[module]) & ptr[1]))) return -1; AddToQueue(module,ptr,size,0); if (PipeMask[module] & M_LOCKONSEND) { Window targetWindow; int e; FlushQueue(module); fcntl(readPipes[module],F_SETFL,0); while ((e = read(readPipes[module],&targetWindow, sizeof(Window))) > 0) { if (HandleModuleInput(targetWindow,module) == 66) break; } if (e <= 0) KillModule(module,10); fcntl(readPipes[module],F_SETFL,O_NDELAY); } return size; } void AddToQueue(int module, unsigned long *ptr, int size, int done) { struct queue_buff_struct *c,*e; unsigned long *d; c = (struct queue_buff_struct *)safemalloc(sizeof(struct queue_buff_struct)); c->next = NULL; c->size = size; c->done = done; d = (unsigned long *)safemalloc(size); c->data = d; memcpy(d,ptr,size); e = pipeQueue[module]; if(e == NULL) { pipeQueue[module] = c; return; } while(e->next != NULL) e = e->next; e->next = c; } void DeleteQueueBuff(int module) { struct queue_buff_struct *a; if(pipeQueue[module] == NULL) return; a = pipeQueue[module]; pipeQueue[module] = a->next; free(a->data); free(a); return; } void FlushQueue(int module) { char *dptr; struct queue_buff_struct *d; int a; extern int errno; if((pipeOn[module] <= 0)||(pipeQueue[module] == NULL)) return; while(pipeQueue[module] != NULL) { d = pipeQueue[module]; dptr = (char *)d->data; while(d->done < d->size) { a = write(writePipes[module],&dptr[d->done], d->size - d->done); if(a >=0) d->done += a; /* the write returns EWOULDBLOCK or EAGAIN if the pipe is full. * (This is non-blocking I/O). SunOS returns EWOULDBLOCK, OSF/1 * returns EAGAIN under these conditions. Hopefully other OSes * return one of these values too. Solaris 2 doesn't seem to have * a man page for write(2) (!) */ else if ((errno == EWOULDBLOCK)||(errno == EAGAIN)||(errno==EINTR)) { return; } else { KillModule(module,123); return; } } DeleteQueueBuff(module); } }