./configure --prefix support for perl
[adesklets.git] / src / cfgfile.c
bloba388357aea7c1f534cc80a112f7890850ac0478c
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 struct passwd * info;
46 TRY_CATCH(info = getpwuid(getuid()));
47 if (info->pw_dir==NULL) return 0L;
48 TRY_CATCH(strncpy(filename,info->pw_dir,CFGFILE_NAME_SIZE-1));
49 TRY_CATCH(strncat(filename,\
50 CFGFILE_NAME,\
51 CFGFILE_NAME_SIZE-strlen(filename)-1));
52 debug("Configuration file name: `%s'\n",filename);
53 return filename;
56 /*----------------------------------------------------------------------------*/
57 /* Initialize and call the configuration file parser.
58 Returns a vector of cfgfile_item structures if successful,
59 NULL otherwise.
61 vector *
62 cfgfile_parse(FILE * fd)
64 #ifdef DEBUG
65 int i;
66 #endif
67 vector * vec;
69 TRY_CATCH(fseek(fd,0L,SEEK_SET));
71 if (cfgparse_init(vec=vector_init())) {
72 cfgin=fd;
73 if (cfgparse()==1) {
74 debug("Error parsing configuration file\n");
75 vec=vector_free(vec);
77 #ifdef DEBUG
78 else {
79 debug("Configuration file parsing results:\n");
80 for (i=0;i<vec->pos;++i)
81 debug("[%s] id=%u screen=%d x=%u y=%u\n",
82 ((cfgfile_item*)vec->content[i])->applet,
83 ((cfgfile_item*)vec->content[i])->id,
84 ((cfgfile_item*)vec->content[i])->scr,
85 ((cfgfile_item*)vec->content[i])->x,
86 ((cfgfile_item*)vec->content[i])->y);
88 #endif
90 return vec;
93 /*----------------------------------------------------------------------------*/
94 /* Open adesklets' configuration file, using a POSIX advisory locking
95 mechanism. Returns reference to FILE if successful, NULL otherwise.
96 Try creating the file if it do not exists.
98 WARNING: this routine will block until all other locks on the file
99 are removed by concurrent process. Therefore, operations
100 between successive calls to cfgfile_open() and cfgfile_close()
101 should stay as short as possible!
103 FILE *
104 cfgfile_open()
106 char * filename;
107 FILE * fd;
108 struct flock lock;
110 fd = NULL;
111 if ((filename=cfgfile_name()))
112 if ((fd=fopen(filename,"r+"))||
113 (fd=fopen(filename,"w+"))) {
114 lock.l_type=F_WRLCK;
115 lock.l_whence=SEEK_SET;
116 lock.l_start=0;
117 lock.l_len=0;
118 TRY_CATCH(fcntl(fileno(fd),F_SETLKW,&lock));
120 return fd;
123 /*----------------------------------------------------------------------------*/
124 /* Close configuration file fd and remove fcntl advisory lock.
125 Returns 1 on success, 0 on failure.
127 int
128 cfgfile_close(FILE * fd)
130 struct flock lock;
132 lock.l_type=F_UNLCK;
133 lock.l_whence=SEEK_SET;
134 lock.l_start=0;
135 lock.l_len=0;
136 TRY_CATCH(fcntl(fileno(fd),F_SETLK,&lock));
137 TRY_CATCH(fclose(fd));
138 return 1L;
141 /*----------------------------------------------------------------------------*/
142 struct s_cfg_search_applet {
143 char * applet;
144 uint id;
147 /*----------------------------------------------------------------------------*/
148 int
149 cfg_search_applet(void * elem, void* callback)
151 return (strncmp(((cfgfile_item*)elem)->applet,
152 ((struct s_cfg_search_applet*)callback)->applet,
153 256) == 0) &&
154 ((cfgfile_item*)elem)->id ==
155 ((struct s_cfg_search_applet*)callback)->id;
158 /*----------------------------------------------------------------------------*/
160 cfg_search_highest_applet(void * elem, void * callback)
162 if (strncmp(((cfgfile_item*)elem)->applet,
163 ((struct s_cfg_search_applet*)callback)->applet,
164 256) == 0 &&
165 (((cfgfile_item*)elem)->id > ((struct s_cfg_search_applet*)callback)->id ||
166 ((struct s_cfg_search_applet*)callback)->id==(uint)-1))
167 ((struct s_cfg_search_applet*)callback)->id=((cfgfile_item*)elem)->id;
168 return 0;
171 /*----------------------------------------------------------------------------*/
172 /* Get an applet caracteristics from configuration file.
173 Always return the reference, dutyfully filled with zeroes
174 if not describded. If given id is ((uint)-1), attribute next
175 free id for applet
177 Note : This code is not made to be used in reentrant context,
178 so feel free to modify returned content.
180 cfgfile_item *
181 cfgfile_getinfo(char * applet, uint id)
183 uint i;
184 char str[2] = "-";
185 FILE * fd;
186 vector * vec;
187 static cfgfile_item item;
188 cfgfile_item * lpitem = NULL;
189 struct s_cfg_search_applet cfg_search;
192 if ((fd=cfgfile_open())) {
193 if ((vec=cfgfile_parse(fd))) {
194 cfgfile_close(fd);
196 cfg_search.applet=(applet)?applet:(char*)&str;
197 if (id!=(uint)-1) {
198 cfg_search.id=id;
199 if ((lpitem = vector_find(vec,&i,cfg_search_applet,&cfg_search))) {
200 strncpy(item.applet,lpitem->applet,CFGFILE_NAME_SIZE-1);
201 item.applet[CFGFILE_NAME_SIZE-1]=0;
202 item.id=lpitem->id;
203 item.scr=lpitem->scr;
204 item.x=lpitem->x;
205 item.y=lpitem->y;
206 item.no_update=lpitem->no_update;
208 } else {
209 if (applet) {
210 cfg_search.id=(uint)-1;
211 vector_find(vec,&i,cfg_search_highest_applet,&cfg_search);
212 id=(uint)(((int)cfg_search.id)+1);
213 debug("New id: %u\n",id);
214 } else id=0;
217 vec=vector_free(vec);
218 } else cfgfile_close(fd);
221 if (!lpitem) {
222 strncpy(item.applet,(applet)?applet:(char*)&str,CFGFILE_NAME_SIZE-1);
223 item.applet[CFGFILE_NAME_SIZE-1]=0;
224 item.id=id;
225 item.x=item.y=item.no_update=0;
226 item.scr=-1; /* Invalid screen reference:
227 force update in adesklets_init()
231 debug("Configuration file info: [%s] id=%u screen=%d x=%d y=%d\n",
232 item.applet, item.id, item.scr, item.x, item.y);
234 return &item;
237 /*----------------------------------------------------------------------------*/
238 /* Update an applet description in configuration file, remove it
239 if no_update flags sets to 1.
240 Return 1 on success, 0 on failure.
243 cfgfile_update(cfgfile_item * new_item)
245 int is_new = 0, result = 1;
246 uint i;
247 FILE * fd;
248 vector * vec;
249 cfgfile_item * item;
250 struct s_cfg_search_applet cfg_search;
252 const char * cfgfile_message[] = {
253 "# This is adesklets configuration file.",
254 "#",
255 "# It gets automatically updated every time a desklet main window",
256 "# parameter is changed, so avoid manual modification whenever",
257 "# a desklet is running. In fact, manual changes to this file",
258 "# should rarely be needed. See `info adesklets'.\n"
261 TRY_RESET;
262 if (new_item->applet) {
263 /* Open the file */
264 if ((fd=cfgfile_open())) {
265 /* Parse the file */
266 if ((vec=cfgfile_parse(fd))) {
267 /* Find item, if it exists */
268 cfg_search.applet=new_item->applet;
269 cfg_search.id=new_item->id;
270 if (!(item = vector_find(vec,&i,cfg_search_applet,&cfg_search)))
271 TRY(item=malloc(sizeof(cfgfile_item));is_new=1);
272 /* And delete it, if asked for */
273 else if (new_item->no_update) {
274 debug("Configuration file update: removing an entry\n");
275 result=vector_delete(vec,i);
277 /* Copy updated data */
278 if (!new_item->no_update && TRY_SUCCESS) {
279 strncpy(item->applet,new_item->applet,256);
280 item->id=new_item->id;
281 item->scr=new_item->scr;
282 item->x=new_item->x;
283 item->y=new_item->y;
284 if(is_new) result=vector_push(vec,item);
285 } else {
286 if (is_new) free(item);
287 if (!TRY_SUCCESS) result=0L;
289 /* Write everything back to configuration file */
290 if (result) {
291 debug("Configuration file: write everything back\n");
292 TRY(fseek(fd,0L,SEEK_SET));
293 if (TRY_SUCCESS) {
294 for (i=0;i<6;++i) fprintf(fd,"%s\n",cfgfile_message[i]);
295 for(i=0;i<vec->pos;++i)
296 fprintf(fd,"[%s]\nid=%u screen=%d x=%u y=%u\n\n",
297 ((cfgfile_item*)vec->content[i])->applet,
298 ((cfgfile_item*)vec->content[i])->id,
299 ((cfgfile_item*)vec->content[i])->scr,
300 ((cfgfile_item*)vec->content[i])->x,
301 ((cfgfile_item*)vec->content[i])->y);
302 if (ftruncate(fileno(fd),ftell(fd))==-1) result=0L;
303 } else result=0L;
305 vector_free(vec);
306 } else result=0L;
307 cfgfile_close(fd);
308 } else result=0L;
309 } else result=0L;
310 return result;
313 /*----------------------------------------------------------------------------*/
314 /* Execute every desklet described in the configuration file,
315 setting the ADESKLETS_ID environment variable as needed.
316 Returns 1 on success, 0 on failure.
319 cfgfile_loadall(void)
321 char * filename;
322 int tmp_result = 0, result = 1, fd_null, pid;
323 uint i;
324 char *dot, * dpy_in, *dpy_out,
325 id_str[10]; /* See cfg_scanner.l for size justification */
326 FILE * fd;
327 struct dirent * entry;
328 DIR * dir;
329 struct flock lock;
330 vector * vec;
332 /* Clean up all lock files */
333 if ((dir=opendir(LOCKFILES_DIR))) {
334 for(;(entry=readdir(dir));)
335 if (strstr(entry->d_name,"adesklets")==entry->d_name) {
336 filename=malloc(sizeof(char)*(strlen(entry->d_name)+
337 strlen(LOCKFILES_DIR)+2));
338 strcpy(filename,LOCKFILES_DIR);
339 strcat(filename,"/");
340 strcat(filename,entry->d_name);
341 if ((fd=fopen(filename,"r+"))) {
342 lock.l_type=F_WRLCK;
343 lock.l_whence=SEEK_SET;
344 lock.l_start=0;
345 lock.l_len=0;
346 fcntl(fileno(fd),F_GETLK,&lock);
347 if (lock.l_type!=F_UNLCK) {
348 /* Action has to be taken to terminate older,
349 still running desklets */
350 debug("desklets process associated with lockfile '%s' %s",
351 filename, "are still runnning.\n");
352 while(fscanf(fd,"%d",&pid)==1)
353 kill(pid,SIGKILL);
355 unlink(filename);
356 debug("Unlinking '%s'\n",filename);
357 fclose(fd);
359 free(filename);
361 closedir(dir);
364 if ((fd=cfgfile_open()))
365 if ((vec=cfgfile_parse(fd))) {
366 cfgfile_close(fd);
367 /* Here we go: let's fork and execute */
368 for(i=0;i<vec->pos;++i)
369 if((result = result && ((tmp_result = fork())>=0)), !tmp_result ) {
370 /* Charge the correct ID into the environment */
371 if(snprintf(id_str,9,"%u",((cfgfile_item*)vec->content[i])->id)>=1)
372 setenv("ADESKLETS_ID",id_str,1);
373 /* Then build up a correct DISPLAY variable (i.e. with proper default
374 screen). Of course, this is redundant, since the adesklets
375 interpreter doesn't need this information; but the wrapper script
376 could, since it may want to find a fake root window ID. Look
377 at the XOpenDisplay() documentation for the complete description
378 of the DISPLAY variable.
380 if ((dpy_in=getenv("DISPLAY")) &&
381 (dot=strrchr(dpy_in,'.')) &&
382 (dpy_out=malloc(strlen(dpy_in)+10))) {
383 strcpy(dpy_out,dpy_in);
384 if (snprintf(dpy_out+(dot-dpy_in)+1,9,"%u",
385 ((cfgfile_item*)vec->content[i])->scr)>=0)
386 setenv("DISPLAY", dpy_out, 1);
387 free(dpy_out);
390 /* Redirect stdout and stderr to /dev/null
391 to avoid possible output overflow */
392 if ((fd_null=open("/dev/null",O_WRONLY))>=0) {
393 dup2(fd_null,1);
394 dup2(fd_null,2);
395 close(fd_null);
397 execl(((cfgfile_item*)vec->content[i])->applet,
398 ((cfgfile_item*)vec->content[i])->applet,
399 NULL);
400 /* debug("Could not exec: `%s'\n",
401 ((cfgfile_item*)vec->content[i])->applet); */
402 exit(EXIT_FAILURE);
404 vector_free(vec);
405 } else {
406 cfgfile_close(fd);
407 result=0L;
409 else result=0L;
411 return result;