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
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
*);
37 /*----------------------------------------------------------------------------*/
38 /* Return canonical filename of configuration file,
39 NULL if it couldn't be retrieved.
43 static char filename
[CFGFILE_NAME_SIZE
] = "";
47 if ((home
= getenv("HOME"))) {
48 TRY_CATCH(strncpy(filename
,home
,CFGFILE_NAME_SIZE
-1));
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
,\
56 CFGFILE_NAME_SIZE
-strlen(filename
)-1));
57 debug("Configuration file name: `%s'\n",filename
);
61 /*----------------------------------------------------------------------------*/
62 /* Initialize and call the configuration file parser.
63 Returns a vector of cfgfile_item structures if successful,
67 cfgfile_parse(FILE * fd
)
74 TRY_CATCH(fseek(fd
,0L,SEEK_SET
));
76 if (cfgparse_init(vec
=vector_init())) {
79 debug("Error parsing configuration file\n");
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
);
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!
116 if ((filename
=cfgfile_name()))
117 if ((fd
=fopen(filename
,"r+"))||
118 (fd
=fopen(filename
,"w+"))) {
120 lock
.l_whence
=SEEK_SET
;
123 TRY_CATCH(fcntl(fileno(fd
),F_SETLKW
,&lock
));
128 /*----------------------------------------------------------------------------*/
129 /* Close configuration file fd and remove fcntl advisory lock.
130 Returns 1 on success, 0 on failure.
133 cfgfile_close(FILE * fd
)
138 lock
.l_whence
=SEEK_SET
;
141 TRY_CATCH(fcntl(fileno(fd
),F_SETLK
,&lock
));
142 TRY_CATCH(fclose(fd
));
146 /*----------------------------------------------------------------------------*/
147 struct s_cfg_search_applet
{
152 /*----------------------------------------------------------------------------*/
154 cfg_search_applet(void * elem
, void* callback
)
156 return (strncmp(((cfgfile_item
*)elem
)->applet
,
157 ((struct s_cfg_search_applet
*)callback
)->applet
,
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
,
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
;
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
182 Note : This code is not made to be used in reentrant context,
183 so feel free to modify returned content.
186 cfgfile_getinfo(char * applet
, uint id
)
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
))) {
201 cfg_search
.applet
=(applet
)?applet
:(char*)&str
;
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;
208 item
.scr
=lpitem
->scr
;
211 item
.no_update
=lpitem
->no_update
;
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
);
222 vec
=vector_free(vec
);
223 } else cfgfile_close(fd
);
227 strncpy(item
.applet
,(applet
)?applet
:(char*)&str
,CFGFILE_NAME_SIZE
-1);
228 item
.applet
[CFGFILE_NAME_SIZE
-1]=0;
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
);
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;
255 struct s_cfg_search_applet cfg_search
;
257 const char * cfgfile_message
[] = {
258 "# This is adesklets configuration file.",
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"
267 if (new_item
->applet
) {
269 if ((fd
=cfgfile_open())) {
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
;
289 if(is_new
) result
=vector_push(vec
,item
);
291 if (is_new
) free(item
);
292 if (!TRY_SUCCESS
) result
=0L;
294 /* Write everything back to configuration file */
296 debug("Configuration file: write everything back\n");
297 TRY(fseek(fd
,0L,SEEK_SET
));
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;
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)
327 int tmp_result
= 0, result
= 1, fd_null
, pid
;
329 char *dot
, * dpy_in
, *dpy_out
,
330 id_str
[10]; /* See cfg_scanner.l for size justification */
332 struct dirent
* entry
;
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+"))) {
348 lock
.l_whence
=SEEK_SET
;
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)
361 debug("Unlinking '%s'\n",filename
);
369 if ((fd
=cfgfile_open()))
370 if ((vec
=cfgfile_parse(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);
395 /* Redirect stdout and stderr to /dev/null
396 to avoid possible output overflow */
397 if ((fd_null
=open("/dev/null",O_WRONLY
))>=0) {
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
,
410 /* debug("Could not exec: `%s'\n",
411 ((cfgfile_item*)vec->content[i])->applet); */