pndnotifyd: fix some crashes
[pandora-libraries.git] / lib / pnd_utility.c
1
2 #include <stdio.h> /* for FILE etc */
3 #include <stdlib.h> /* for malloc */
4 #define __USE_GNU /* for strcasestr */
5 #include <string.h> /* for making ftw.h happy */
6 #include <unistd.h> /* for fork exec */
7
8 #include <utmp.h> /* for expand-tilde below; see commentary for this about-turn */
9 #include <sys/types.h> /* ditto */
10 #include <pwd.h> /* ditto */
11 #include <sys/stat.h> /* for fstat */
12 #include <errno.h> /* for stat->ENOENT */
13 #include <dirent.h> /* for opendir */
14 #include <fcntl.h> /* for creat */
15
16 #include "pnd_pxml.h"
17 #include "pnd_container.h"
18 #include "pnd_utility.h"
19 #include "pnd_pndfiles.h"
20 #include "pnd_discovery.h"
21
22 unsigned char pnd_check_login ( char *r_username, unsigned int maxlen ) {
23   FILE *f;
24   struct utmp b;
25   struct passwd *pw;
26
27   f = fopen ( "/var/run/utmp", "r" );
28
29   if ( f ) {
30
31     // spin until a non-root user comes along
32     while ( fread ( &b, sizeof(struct utmp), 1, f ) == 1 ) {
33
34       if ( ( b.ut_type == USER_PROCESS ) &&
35            ( strcmp ( b.ut_user, "root" ) != 0 ) )
36       {
37
38         // ut_user contains the username ..
39         // now we need to find the path to that account.
40         while ( ( pw = getpwent() ) ) {
41
42           if ( strcmp ( pw -> pw_name, b.ut_user ) == 0 ) {
43
44             if ( r_username ) {
45               strncpy ( r_username, b.ut_user, maxlen );
46             }
47
48             fclose ( f );
49             return ( 1 );
50           } // passwd entry matches the utmp entry
51
52         } // while iteratin across passwd entries
53         endpwent();
54
55       } // utmp entry is for a user login
56
57     } // while
58
59     fclose ( f );
60   } // opened?
61
62   return ( 0 );
63 }
64
65 // a generalized variable-substitution routine might be nice; for now we need a quick tilde one,
66 // so here goes. Brute force ftw!
67 char *pnd_expand_tilde ( char *freeable_buffer ) {
68   char *p;
69   char *s = freeable_buffer;
70   char *home = getenv ( "HOME" );
71
72   //printf ( "DEBUG: expand tilde IN: '%s'\n", freeable_buffer );
73   //printf ( "DEBUG:  $HOME was %s\n", home );
74
75   // well, as pndnotifyd (etc) may be running as _root_, while the user is logged in
76   // as 'pandora' or god knows what, this could be problematic. Other parts of the lib
77   // use wordexp() for shell-like expansion, but this funciton is (at least) used by
78   // pndnotifyd for determination of the .desktop emit path, so we need ~ to expand
79   // to the _actual user_ rather than root's homedir. Rather than run 'users' or 'who'
80   // or the like, we'll just cut to the chase..
81   ///////
82   {
83     FILE *f;
84     struct utmp b;
85     static char *florp = NULL;
86     struct passwd *pw;
87
88     f = fopen ( "/var/run/utmp", "r" );
89
90     if ( f ) {
91
92       while ( fread ( &b, sizeof(struct utmp), 1, f ) == 1 ) {
93
94         if ( b.ut_type == USER_PROCESS ) {
95
96           // ut_user contains the username ..
97           // now we need to find the path to that account.
98           while ( ( pw = getpwent() ) ) {
99
100             if ( strcmp ( pw -> pw_name, b.ut_user ) == 0 ) {
101
102               // aight, we've got a logged in user and have matched it to
103               // passwd entry, so can construct the appropriate path
104
105               if ( florp ) {
106                 free ( florp );
107               }
108               florp = strdup ( pw -> pw_dir );
109
110               home = florp;
111               //printf ( "  DEBUG: home (for %s) is %s (from %u)\n", b.ut_user, home, b.ut_type );
112
113             } // passwd entry matches the utmp entry
114
115           } // while iteratin across passwd entries
116           endpwent();
117
118         } // utmp entry is for a user login
119
120       } // while
121       fclose ( f );
122     } // opened?
123
124   }
125   ///////
126
127   if ( ! home ) {
128     return ( s ); // can't succeed
129   }
130
131   //printf ( "DEBUG: entering while (%s) with home (%s)\n", s, home );
132
133   while ( ( p = strchr ( s, '~' ) ) ) {
134     //printf ( "DEBUG: within while (%s)\n", s );
135     char *temp = malloc ( strlen ( s ) + strlen ( home ) + 1 );
136     memset ( temp, '\0', strlen ( s ) + strlen ( home ) + 1 );
137     // copy in stuff prior to ~
138     strncpy ( temp, s, p - s );
139     // copy tilde in
140     strcat ( temp, home );
141     // copy stuff after tilde in
142     strcat ( temp, p + 1 );
143     // swap ptrs
144     free ( s );
145     s = temp;
146   } // while finding matches
147
148   //printf ( "DEBUG: expand tilde OUT: '%s'\n", s );
149
150   return ( s );
151 }
152
153 void pnd_exec_no_wait_1 ( char *fullpath, char *arg1 ) {
154   int i;
155
156   if ( ( i = fork() ) < 0 ) {
157     printf ( "ERROR: Couldn't fork()\n" );
158     return;
159   }
160
161   if ( i ) {
162     return; // parent process, don't care
163   }
164
165   // child process, do something
166   if ( arg1 ) {
167     execl ( fullpath, fullpath, arg1, (char*) NULL );
168   } else {
169     execl ( fullpath, fullpath, (char*) NULL );
170   }
171
172   // error invoking something, and we're the child process, so just die before all hell breaks lose with us thinking we're the (second!) parent on return!
173   exit ( -1 );
174
175   // getting here is an error
176   //printf ( "Error attempting to run %s\n", fullpath );
177
178   return;
179 }
180
181 pnd_pxml_handle *pnd_pxml_get_by_path ( char *fullpath ) {
182   unsigned char valid = pnd_object_type_unknown;
183   pnd_pxml_handle *pxmlapps = 0;
184
185   // WARN: this is way too close to callback in pnd_disco .. should be refactored!
186
187   if ( strcasestr ( fullpath, PXML_FILENAME ) ) {
188     valid = pnd_object_type_directory;
189   } else if ( strcasestr ( fullpath, PND_PACKAGE_FILEEXT "\0" ) ) {
190     valid = pnd_object_type_pnd;
191   }
192
193   // if not a file of interest, just keep looking until we run out
194   if ( ! valid ) {
195     return ( 0 );
196   }
197
198   // potentially a valid application
199   if ( valid == pnd_object_type_directory ) {
200     pxmlapps = pnd_pxml_fetch ( (char*) fullpath );
201
202   } else if ( valid == pnd_object_type_pnd ) {
203     FILE *f;
204     char pxmlbuf [ 32 * 1024 ]; // TBD: assuming 32k pxml accrual buffer is a little lame
205
206     // open it up..
207     f = fopen ( fullpath, "r" );
208
209     // try to locate the PXML portion
210     if ( ! pnd_pnd_seek_pxml ( f ) ) {
211       fclose ( f );
212       return ( 0 ); // pnd or not, but not to spec. Pwn'd the pnd?
213     }
214
215     // accrue it into a buffer
216     if ( ! pnd_pnd_accrue_pxml ( f, pxmlbuf, 32 * 1024 ) ) {
217       fclose ( f );
218       return ( 0 );
219     }
220
221     // by now, we have <PXML> .. </PXML>, try to parse..
222     pxmlapps = pnd_pxml_fetch_buffer ( (char*) fullpath, pxmlbuf );
223
224     // done with file
225     fclose ( f );
226
227   }
228
229   // ..
230
231   return ( pxmlapps );
232 }
233
234 unsigned char pnd_determine_mountpoint ( char *fullpath, char *r_mountpoint, unsigned int mountpoint_len ) {
235
236   // just cheap it, and call df like an idiot.
237
238   // Filesystem           1K-blocks      Used Available Use% Mounted on
239
240   char cmd [ PATH_MAX ];
241   FILE *p;
242   char inbuf [ PATH_MAX ];
243
244   sprintf ( cmd, "/bin/df %s 2>/dev/null", fullpath );
245
246   if ( ( p = popen ( cmd, "r" ) ) ) {
247
248     // ignore title line; we really shoudl analyze it to figure out which column, but we make assumptions..
249     fgets ( inbuf, PATH_MAX, p );
250
251     if ( ! fgets ( inbuf, PATH_MAX, p ) ) {
252       pclose ( p );
253       return ( 0 );
254     }
255
256     pclose ( p );
257
258     // by now, good
259     char mount [ PATH_MAX ];
260     if ( sscanf ( inbuf, "%*s %*s %*s %*s %*s %s", mount ) != 1 ) {
261       return ( 0 );
262     }
263
264     if ( strlen ( mount ) < mountpoint_len ) {
265       strcpy ( r_mountpoint, mount );
266       return ( 1 );
267     }
268
269   } // if popen
270
271   return ( 0 );
272
273 #if 0
274   struct stat fooby;
275
276   // can we even stat this file?
277   if ( stat ( fullpath, &fooby ) == 0 ) {
278     //dev_t     st_dev;     /* ID of device containing file */
279     //dev_t     st_rdev;    /* device ID (if special file) */
280
281     dev_t mount = fooby.st_dev;
282
283     DIR *d = opendir ( "/dev" );
284
285     if ( d ) {
286       struct dirent *de;
287       char path [ FILENAME_MAX ];
288
289       while ( de = readdir ( d ) ) {
290         sprintf ( path, "/dev/%s", de -> d_name );
291
292         if ( stat ( path, &fooby ) == 0 ) {
293
294           // finally, if we find the same major/minor pair in /dev, as we found for the target file, it means we found the right device
295           if ( fooby.st_rdev == mount ) {
296             printf ( "Device: %s\n", path );
297           }
298
299         } // if
300
301       } // while
302
303     } // opened /dev?
304
305   } // stat
306 #endif
307
308   return ( 0 );
309 }
310
311 unsigned char pnd_filecopy ( char *sourcepath, char *targetpath ) {
312 #define BITLEN (64*1024)
313   FILE *pnd, *target; // pnd == from, since I cribbed the code from pnd_desktop.c :/
314   unsigned char bits [ BITLEN ];
315   unsigned int bitlen;
316
317   pnd = fopen ( sourcepath, "rb" );
318
319   if ( ! pnd ) {
320     return ( 0 );
321   }
322
323   unsigned int len;
324
325   target = fopen ( targetpath, "wb" );
326
327   if ( ! target ) {
328     fclose ( pnd );
329     return ( 0 );
330   }
331
332   fseek ( pnd, 0, SEEK_END );
333   len = ftell ( pnd );
334   fseek ( pnd, 0, SEEK_SET );
335
336   while ( len ) {
337
338     if ( len > (BITLEN) ) {
339       bitlen = (BITLEN);
340     } else {
341       bitlen = len;
342     }
343
344     if ( fread ( bits, bitlen, 1, pnd ) != 1 ) {
345       fclose ( pnd );
346       fclose ( target );
347       unlink ( targetpath );
348       return ( 0 );
349     }
350
351     if ( fwrite ( bits, bitlen, 1, target ) != 1 ) {
352       fclose ( pnd );
353       fclose ( target );
354       unlink ( targetpath );
355       return ( 0 );
356     }
357
358     len -= bitlen;
359   } // while
360
361   fclose ( pnd );
362   fclose ( target );
363
364   return ( 1 );
365 }
366
367 unsigned char pnd_lock ( char *lockname ) {
368
369   if ( pnd_is_locked ( lockname ) ) {
370     return ( 0 ); // already locked
371   }
372
373   char fullpath [ PATH_MAX ];
374   int fd;
375
376   snprintf ( fullpath, PATH_MAX, "%s/%s", PND_LOCK_PATH, lockname );
377
378   if ( ( fd = creat ( fullpath, 0444 ) ) < 0 ) {
379     return ( 0 ); // error, yeah, I know, no way to know why it failed..
380   }
381
382   close ( fd );
383
384   return ( 1 );
385 }
386
387 time_t pnd_is_locked ( char *lockname ) {
388   char fullpath [ PATH_MAX ];
389   int rv;
390   snprintf ( fullpath, PATH_MAX, "%s/%s", PND_LOCK_PATH, lockname );
391
392   struct stat statbuf;
393   rv = stat ( fullpath, &statbuf );
394
395   if ( rv == ENOENT ) {
396     return ( 0 ); // file not existk, so no lock
397   } else if ( rv < 0 ) {
398     return ( 0 ); // assume unlocked for error, so app can continue?
399   }
400
401   return ( statbuf.st_mtime );
402 }
403
404 void pnd_unlock ( char *lockname ) {
405   char fullpath [ PATH_MAX ];
406   snprintf ( fullpath, PATH_MAX, "%s/%s", PND_LOCK_PATH, lockname );
407
408   unlink ( fullpath );
409
410   return;
411 }
412
413 unsigned char pnd_wait_for_unlock ( char *lockname, unsigned short int max, unsigned int usec_delta ) {
414
415   // check right off the top
416   if ( ! pnd_is_locked ( lockname ) ) {
417     return ( 1 ); // all clear!
418   }
419
420   unsigned short int count = 0;
421   while ( count < max ) {
422
423     if ( ! pnd_is_locked ( lockname ) ) {
424       return ( 1 ); // all clear!
425     }
426
427     usleep ( usec_delta );
428
429     count++;
430   }
431
432   return ( 0 );
433 }