1 /*--- cfgfile.c ----------------------------------------------------------------
2 Copyright (C) 2004, 2005 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
] = "";
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
,\
51 CFGFILE_NAME_SIZE
-strlen(filename
)-1));
52 debug("Configuration file name: `%s'\n",filename
);
56 /*----------------------------------------------------------------------------*/
57 /* Initialize and call the configuration file parser.
58 Returns a vector of cfgfile_item structures if successful,
62 cfgfile_parse(FILE * fd
)
69 TRY_CATCH(fseek(fd
,0L,SEEK_SET
));
71 if (cfgparse_init(vec
=vector_init())) {
74 debug("Error parsing configuration file\n");
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
);
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!
111 if ((filename
=cfgfile_name()))
112 if ((fd
=fopen(filename
,"r+"))||
113 (fd
=fopen(filename
,"w+"))) {
115 lock
.l_whence
=SEEK_SET
;
118 TRY_CATCH(fcntl(fileno(fd
),F_SETLKW
,&lock
));
123 /*----------------------------------------------------------------------------*/
124 /* Close configuration file fd and remove fcntl advisory lock.
125 Returns 1 on success, 0 on failure.
128 cfgfile_close(FILE * fd
)
133 lock
.l_whence
=SEEK_SET
;
136 TRY_CATCH(fcntl(fileno(fd
),F_SETLK
,&lock
));
137 TRY_CATCH(fclose(fd
));
141 /*----------------------------------------------------------------------------*/
142 struct s_cfg_search_applet
{
147 /*----------------------------------------------------------------------------*/
149 cfg_search_applet(void * elem
, void* callback
)
151 return (strncmp(((cfgfile_item
*)elem
)->applet
,
152 ((struct s_cfg_search_applet
*)callback
)->applet
,
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
,
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
;
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
177 Note : This code is not made to be used in reentrant context,
178 so feel free to modify returned content.
181 cfgfile_getinfo(char * applet
, uint id
)
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
))) {
196 cfg_search
.applet
=(applet
)?applet
:(char*)&str
;
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;
203 item
.scr
=lpitem
->scr
;
206 item
.no_update
=lpitem
->no_update
;
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
);
217 vec
=vector_free(vec
);
218 } else cfgfile_close(fd
);
222 strncpy(item
.applet
,(applet
)?applet
:(char*)&str
,CFGFILE_NAME_SIZE
-1);
223 item
.applet
[CFGFILE_NAME_SIZE
-1]=0;
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
);
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;
250 struct s_cfg_search_applet cfg_search
;
252 const char * cfgfile_message
[] = {
253 "# This is adesklets configuration file.",
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"
262 if (new_item
->applet
) {
264 if ((fd
=cfgfile_open())) {
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
;
284 if(is_new
) result
=vector_push(vec
,item
);
286 if (is_new
) free(item
);
287 if (!TRY_SUCCESS
) result
=0L;
289 /* Write everything back to configuration file */
291 debug("Configuration file: write everything back\n");
292 TRY(fseek(fd
,0L,SEEK_SET
));
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;
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)
322 int tmp_result
= 0, result
= 1, fd_null
, pid
;
324 char *dot
, * dpy_in
, *dpy_out
,
325 id_str
[10]; /* See cfg_scanner.l for size justification */
327 struct dirent
* entry
;
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+"))) {
343 lock
.l_whence
=SEEK_SET
;
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)
356 debug("Unlinking '%s'\n",filename
);
364 if ((fd
=cfgfile_open()))
365 if ((vec
=cfgfile_parse(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);
390 /* Redirect stdout and stderr to /dev/null
391 to avoid possible output overflow */
392 if ((fd_null
=open("/dev/null",O_WRONLY
))>=0) {
397 execl(((cfgfile_item
*)vec
->content
[i
])->applet
,
398 ((cfgfile_item
*)vec
->content
[i
])->applet
,
400 /* debug("Could not exec: `%s'\n",
401 ((cfgfile_item*)vec->content[i])->applet); */