Site: new desklet, pacmansentry 0.0.1
[adesklets.git] / src / cfgfile.c
blob008cfb8fcec5b01f7946dcc183d149831bc8c20a
1 /*--- cfgfile.c ----------------------------------------------------------------
2 Copyright (C) 2004, 2005, 2006 Sylvain Fourmanoit <syfou@users.sourceforge.net>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to
6 deal in the Software without restriction, including without limitation the
7 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 sell copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies of the Software and its documentation and acknowledgment shall be
13 given in the documentation and software packages that this Software was
14 used.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 ------------------------------------------------------------------------------*/
23 /* Management routines for adesklets configuration file
26 /*----------------------------------------------------------------------------*/
27 #include "cfgfile.h" /* Adesklets configuration file header */
29 /*----------------------------------------------------------------------------*/
30 /* Useful prototypes from the flex scanner and bison parser
32 int cfgparse_init(vector *);
33 int cfgparse(void);
35 extern FILE * cfgin;
37 /*----------------------------------------------------------------------------*/
38 /* Return canonical filename of configuration file,
39 NULL if it couldn't be retrieved.
41 char * const
42 cfgfile_name(void) {
43 static char filename[CFGFILE_NAME_SIZE] = "";
44 char * home;
45 struct passwd * info;
47 if ((home = getenv("HOME"))) {
48 TRY_CATCH(strncpy(filename,home,CFGFILE_NAME_SIZE-1));
49 } else {
50 TRY_CATCH(info = getpwuid(getuid()));
51 if (info->pw_dir==NULL) return 0L;
52 TRY_CATCH(strncpy(filename,info->pw_dir,CFGFILE_NAME_SIZE-1));
54 TRY_CATCH(strncat(filename,\
55 CFGFILE_NAME,\
56 CFGFILE_NAME_SIZE-strlen(filename)-1));
57 debug("Configuration file name: `%s'\n",filename);
58 return filename;
61 /*----------------------------------------------------------------------------*/
62 /* Initialize and call the configuration file parser.
63 Returns a vector of cfgfile_item structures if successful,
64 NULL otherwise.
66 vector *
67 cfgfile_parse(FILE * fd)
69 #ifdef DEBUG
70 int i;
71 #endif
72 vector * vec;
74 TRY_CATCH(fseek(fd,0L,SEEK_SET));
76 if (cfgparse_init(vec=vector_init())) {
77 cfgin=fd;
78 if (cfgparse()==1) {
79 debug("Error parsing configuration file\n");
80 vec=vector_free(vec);
82 #ifdef DEBUG
83 else {
84 debug("Configuration file parsing results:\n");
85 for (i=0;i<vec->pos;++i)
86 debug("[%s] id=%u screen=%d x=%u y=%u\n",
87 ((cfgfile_item*)vec->content[i])->applet,
88 ((cfgfile_item*)vec->content[i])->id,
89 ((cfgfile_item*)vec->content[i])->scr,
90 ((cfgfile_item*)vec->content[i])->x,
91 ((cfgfile_item*)vec->content[i])->y);
93 #endif
95 return vec;
98 /*----------------------------------------------------------------------------*/
99 /* Open adesklets' configuration file, using a POSIX advisory locking
100 mechanism. Returns reference to FILE if successful, NULL otherwise.
101 Try creating the file if it do not exists.
103 WARNING: this routine will block until all other locks on the file
104 are removed by concurrent process. Therefore, operations
105 between successive calls to cfgfile_open() and cfgfile_close()
106 should stay as short as possible!
108 FILE *
109 cfgfile_open()
111 char * filename;
112 FILE * fd;
113 struct flock lock;
115 fd = NULL;
116 if ((filename=cfgfile_name()))
117 if ((fd=fopen(filename,"r+"))||
118 (fd=fopen(filename,"w+"))) {
119 lock.l_type=F_WRLCK;
120 lock.l_whence=SEEK_SET;
121 lock.l_start=0;
122 lock.l_len=0;
123 TRY_CATCH(fcntl(fileno(fd),F_SETLKW,&lock));
125 return fd;
128 /*----------------------------------------------------------------------------*/
129 /* Close configuration file fd and remove fcntl advisory lock.
130 Returns 1 on success, 0 on failure.
132 int
133 cfgfile_close(FILE * fd)
135 struct flock lock;
137 lock.l_type=F_UNLCK;
138 lock.l_whence=SEEK_SET;
139 lock.l_start=0;
140 lock.l_len=0;
141 TRY_CATCH(fcntl(fileno(fd),F_SETLK,&lock));
142 TRY_CATCH(fclose(fd));
143 return 1L;
146 /*----------------------------------------------------------------------------*/
147 struct s_cfg_search_applet {
148 char * applet;
149 uint id;
152 /*----------------------------------------------------------------------------*/
153 int
154 cfg_search_applet(void * elem, void* callback)
156 return (strncmp(((cfgfile_item*)elem)->applet,
157 ((struct s_cfg_search_applet*)callback)->applet,
158 256) == 0) &&
159 ((cfgfile_item*)elem)->id ==
160 ((struct s_cfg_search_applet*)callback)->id;
163 /*----------------------------------------------------------------------------*/
165 cfg_search_highest_applet(void * elem, void * callback)
167 if (strncmp(((cfgfile_item*)elem)->applet,
168 ((struct s_cfg_search_applet*)callback)->applet,
169 256) == 0 &&
170 (((cfgfile_item*)elem)->id > ((struct s_cfg_search_applet*)callback)->id ||
171 ((struct s_cfg_search_applet*)callback)->id==(uint)-1))
172 ((struct s_cfg_search_applet*)callback)->id=((cfgfile_item*)elem)->id;
173 return 0;
176 /*----------------------------------------------------------------------------*/
177 /* Get an applet caracteristics from configuration file.
178 Always return the reference, dutyfully filled with zeroes
179 if not describded. If given id is ((uint)-1), attribute next
180 free id for applet
182 Note : This code is not made to be used in reentrant context,
183 so feel free to modify returned content.
185 cfgfile_item *
186 cfgfile_getinfo(char * applet, uint id)
188 uint i;
189 char str[2] = "-";
190 FILE * fd;
191 vector * vec;
192 static cfgfile_item item;
193 cfgfile_item * lpitem = NULL;
194 struct s_cfg_search_applet cfg_search;
197 if ((fd=cfgfile_open())) {
198 if ((vec=cfgfile_parse(fd))) {
199 cfgfile_close(fd);
201 cfg_search.applet=(applet)?applet:(char*)&str;
202 if (id!=(uint)-1) {
203 cfg_search.id=id;
204 if ((lpitem = vector_find(vec,&i,cfg_search_applet,&cfg_search))) {
205 strncpy(item.applet,lpitem->applet,CFGFILE_NAME_SIZE-1);
206 item.applet[CFGFILE_NAME_SIZE-1]=0;
207 item.id=lpitem->id;
208 item.scr=lpitem->scr;
209 item.x=lpitem->x;
210 item.y=lpitem->y;
211 item.no_update=lpitem->no_update;
213 } else {
214 if (applet) {
215 cfg_search.id=(uint)-1;
216 vector_find(vec,&i,cfg_search_highest_applet,&cfg_search);
217 id=(uint)(((int)cfg_search.id)+1);
218 debug("New id: %u\n",id);
219 } else id=0;
222 vec=vector_free(vec);
223 } else cfgfile_close(fd);
226 if (!lpitem) {
227 strncpy(item.applet,(applet)?applet:(char*)&str,CFGFILE_NAME_SIZE-1);
228 item.applet[CFGFILE_NAME_SIZE-1]=0;
229 item.id=id;
230 item.x=item.y=item.no_update=0;
231 item.scr=-1; /* Invalid screen reference:
232 force update in adesklets_init()
236 debug("Configuration file info: [%s] id=%u screen=%d x=%d y=%d\n",
237 item.applet, item.id, item.scr, item.x, item.y);
239 return &item;
242 /*----------------------------------------------------------------------------*/
243 /* Update an applet description in configuration file, remove it
244 if no_update flags sets to 1.
245 Return 1 on success, 0 on failure.
248 cfgfile_update(cfgfile_item * new_item)
250 int is_new = 0, result = 1;
251 uint i;
252 FILE * fd;
253 vector * vec;
254 cfgfile_item * item;
255 struct s_cfg_search_applet cfg_search;
257 const char * cfgfile_message[] = {
258 "# This is adesklets configuration file.",
259 "#",
260 "# It gets automatically updated every time a desklet main window",
261 "# parameter is changed, so avoid manual modification whenever",
262 "# a desklet is running. In fact, manual changes to this file",
263 "# should rarely be needed. See `info adesklets'.\n"
266 TRY_RESET;
267 if (new_item->applet) {
268 /* Open the file */
269 if ((fd=cfgfile_open())) {
270 /* Parse the file */
271 if ((vec=cfgfile_parse(fd))) {
272 /* Find item, if it exists */
273 cfg_search.applet=new_item->applet;
274 cfg_search.id=new_item->id;
275 if (!(item = vector_find(vec,&i,cfg_search_applet,&cfg_search)))
276 TRY(item=malloc(sizeof(cfgfile_item));is_new=1);
277 /* And delete it, if asked for */
278 else if (new_item->no_update) {
279 debug("Configuration file update: removing an entry\n");
280 result=vector_delete(vec,i);
282 /* Copy updated data */
283 if (!new_item->no_update && TRY_SUCCESS) {
284 strncpy(item->applet,new_item->applet,256);
285 item->id=new_item->id;
286 item->scr=new_item->scr;
287 item->x=new_item->x;
288 item->y=new_item->y;
289 if(is_new) result=vector_push(vec,item);
290 } else {
291 if (is_new) free(item);
292 if (!TRY_SUCCESS) result=0L;
294 /* Write everything back to configuration file */
295 if (result) {
296 debug("Configuration file: write everything back\n");
297 TRY(fseek(fd,0L,SEEK_SET));
298 if (TRY_SUCCESS) {
299 for (i=0;i<6;++i) fprintf(fd,"%s\n",cfgfile_message[i]);
300 for(i=0;i<vec->pos;++i)
301 fprintf(fd,"[%s]\nid=%u screen=%d x=%u y=%u\n\n",
302 ((cfgfile_item*)vec->content[i])->applet,
303 ((cfgfile_item*)vec->content[i])->id,
304 ((cfgfile_item*)vec->content[i])->scr,
305 ((cfgfile_item*)vec->content[i])->x,
306 ((cfgfile_item*)vec->content[i])->y);
307 if (ftruncate(fileno(fd),ftell(fd))==-1) result=0L;
308 } else result=0L;
310 vector_free(vec);
311 } else result=0L;
312 cfgfile_close(fd);
313 } else result=0L;
314 } else result=0L;
315 return result;
318 /*----------------------------------------------------------------------------*/
319 /* Execute every desklet described in the configuration file,
320 setting the ADESKLETS_ID environment variable as needed.
321 Returns 1 on success, 0 on failure.
324 cfgfile_loadall(void)
326 char * filename;
327 int tmp_result = 0, result = 1, fd_null, pid;
328 uint i;
329 char *dot, * dpy_in, *dpy_out,
330 id_str[10]; /* See cfg_scanner.l for size justification */
331 FILE * fd;
332 struct dirent * entry;
333 DIR * dir;
334 struct flock lock;
335 vector * vec;
337 /* Clean up all lock files */
338 if ((dir=opendir(LOCKFILES_DIR))) {
339 for(;(entry=readdir(dir));)
340 if (strstr(entry->d_name,"adesklets")==entry->d_name) {
341 filename=malloc(sizeof(char)*(strlen(entry->d_name)+
342 strlen(LOCKFILES_DIR)+2));
343 strcpy(filename,LOCKFILES_DIR);
344 strcat(filename,"/");
345 strcat(filename,entry->d_name);
346 if ((fd=fopen(filename,"r+"))) {
347 lock.l_type=F_WRLCK;
348 lock.l_whence=SEEK_SET;
349 lock.l_start=0;
350 lock.l_len=0;
351 fcntl(fileno(fd),F_GETLK,&lock);
352 if (lock.l_type!=F_UNLCK) {
353 /* Action has to be taken to terminate older,
354 still running desklets */
355 debug("desklets process associated with lockfile '%s' %s",
356 filename, "are still runnning.\n");
357 while(fscanf(fd,"%d",&pid)==1)
358 kill(pid,SIGKILL);
360 unlink(filename);
361 debug("Unlinking '%s'\n",filename);
362 fclose(fd);
364 free(filename);
366 closedir(dir);
369 if ((fd=cfgfile_open()))
370 if ((vec=cfgfile_parse(fd))) {
371 cfgfile_close(fd);
372 /* Here we go: let's fork and execute */
373 for(i=0;i<vec->pos;++i)
374 if((result = result && ((tmp_result = fork())>=0)), !tmp_result ) {
375 /* Charge the correct ID into the environment */
376 if(snprintf(id_str,9,"%u",((cfgfile_item*)vec->content[i])->id)>=1)
377 setenv("ADESKLETS_ID",id_str,1);
378 /* Then build up a correct DISPLAY variable (i.e. with proper default
379 screen). Of course, this is redundant, since the adesklets
380 interpreter doesn't need this information; but the wrapper script
381 could, since it may want to find a fake root window ID. Look
382 at the XOpenDisplay() documentation for the complete description
383 of the DISPLAY variable.
385 if ((dpy_in=getenv("DISPLAY")) &&
386 (dot=strrchr(dpy_in,'.')) &&
387 (dpy_out=malloc(strlen(dpy_in)+10))) {
388 strcpy(dpy_out,dpy_in);
389 if (snprintf(dpy_out+(dot-dpy_in)+1,9,"%u",
390 ((cfgfile_item*)vec->content[i])->scr)>=0)
391 setenv("DISPLAY", dpy_out, 1);
392 free(dpy_out);
395 /* Redirect stdout and stderr to /dev/null
396 to avoid possible output overflow */
397 if ((fd_null=open("/dev/null",O_WRONLY))>=0) {
398 dup2(fd_null,1);
399 dup2(fd_null,2);
400 close(fd_null);
403 /* Try changing to the base directory of the desklet
405 chdir(dirname(((cfgfile_item*)vec->content[i])->applet));
407 execl(((cfgfile_item*)vec->content[i])->applet,
408 ((cfgfile_item*)vec->content[i])->applet,
409 NULL);
410 /* debug("Could not exec: `%s'\n",
411 ((cfgfile_item*)vec->content[i])->applet); */
412 exit(EXIT_FAILURE);
414 vector_free(vec);
415 } else {
416 cfgfile_close(fd);
417 result=0L;
419 else result=0L;
421 return result;