• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-cleanup-sockets.c  dbus-cleanup-sockets utility
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  * Copyright (C) 2002 Michael Meeks
6  *
7  * Note that this file is NOT licensed under the Academic Free License,
8  * as it is based on linc-cleanup-sockets which is LGPL.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25 #include <config.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <dirent.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #ifndef TRUE
39 #define TRUE (1)
40 #endif
41 
42 #ifndef FALSE
43 #define FALSE (0)
44 #endif
45 
46 #ifndef NULL
47 #define NULL ((void*) 0)
48 #endif
49 
50 static void*
xmalloc(size_t bytes)51 xmalloc (size_t bytes)
52 {
53   void *mem;
54 
55   if (bytes == 0)
56     return NULL;
57 
58   mem = malloc (bytes);
59 
60   if (mem == NULL)
61     {
62       fprintf (stderr, "Allocation of %d bytes failed\n",
63                (int) bytes);
64       exit (1);
65     }
66 
67   return mem;
68 }
69 
70 static void*
xrealloc(void * old,size_t bytes)71 xrealloc (void *old, size_t bytes)
72 {
73   void *mem;
74 
75   if (bytes == 0)
76     {
77       free (old);
78       return NULL;
79     }
80 
81   mem = realloc (old, bytes);
82 
83   if (mem == NULL)
84     {
85       fprintf (stderr, "Reallocation of %d bytes failed\n",
86                (int) bytes);
87       exit (1);
88     }
89 
90   return mem;
91 }
92 
93 #ifdef AF_UNIX
94 
95 typedef enum
96   {
97     SOCKET_UNKNOWN,
98     SOCKET_FAILED_TO_HANDLE,
99     SOCKET_DEAD,
100     SOCKET_ALIVE,
101     SOCKET_UNLINKED
102   } SocketStatus;
103 
104 static int alive_count = 0;
105 static int cleaned_count = 0;
106 static int unhandled_count = 0;
107 
108 typedef struct
109 {
110   char *name;
111   int   fd;
112   SocketStatus status;
113   int   n_retries;
114 } SocketEntry;
115 
116 static SocketEntry*
socket_entry_new(const char * dir,const char * fname)117 socket_entry_new (const char *dir,
118                   const char *fname)
119 {
120   SocketEntry *se;
121   int len;
122 
123   se = xmalloc (sizeof (SocketEntry));
124 
125   len = strlen (dir) + strlen (fname) + 2; /* 2 = nul and '/' */
126   se->name = xmalloc (len);
127 
128   strcpy (se->name, dir);
129   strcat (se->name, "/");
130   strcat (se->name, fname);
131 
132   se->fd = -1;
133 
134   se->status = SOCKET_UNKNOWN;
135 
136   se->n_retries = 0;
137 
138   return se;
139 }
140 
141 #if 0
142 static void
143 free_socket_entry (SocketEntry *se)
144 {
145   free (se->name);
146   if (se->fd >= 0)
147     close (se->fd);
148   free (se);
149 }
150 #endif
151 
152 static void
read_sockets(const char * dir,SocketEntry *** entries_p,int * n_entries_p)153 read_sockets (const char    *dir,
154               SocketEntry ***entries_p,
155               int           *n_entries_p)
156 {
157   DIR   *dirh;
158   struct dirent *dent;
159   SocketEntry **entries;
160   int n_entries;
161   int allocated;
162 
163   n_entries = 0;
164   allocated = 2;
165   entries = xmalloc (sizeof (SocketEntry*) * allocated);
166 
167   dirh = opendir (dir);
168   if (dirh == NULL)
169     {
170       fprintf (stderr, "Failed to open directory %s: %s\n",
171                dir, strerror (errno));
172       exit (1);
173     }
174 
175   while ((dent = readdir (dirh)))
176     {
177       SocketEntry *se;
178 
179       if (strncmp (dent->d_name, "dbus-", 5) != 0)
180         continue;
181 
182       se = socket_entry_new (dir, dent->d_name);
183 
184       if (n_entries == allocated)
185         {
186           allocated *= 2;
187           entries = xrealloc (entries, sizeof (SocketEntry*) * allocated);
188         }
189 
190       entries[n_entries] = se;
191       n_entries += 1;
192     }
193 
194   closedir (dirh);
195 
196   *entries_p = entries;
197   *n_entries_p = n_entries;
198 }
199 
200 static SocketStatus
open_socket(SocketEntry * se)201 open_socket (SocketEntry *se)
202 {
203   int ret;
204   struct sockaddr_un saddr;
205 
206   if (se->n_retries > 5)
207     {
208       fprintf (stderr, "Warning: giving up on socket %s after several retries; unable to determine socket's status\n",
209                se->name);
210       return SOCKET_FAILED_TO_HANDLE;
211     }
212 
213   se->n_retries += 1;
214 
215   se->fd = socket (AF_UNIX, SOCK_STREAM, 0);
216   if (se->fd < 0)
217     {
218       fprintf (stderr, "Warning: failed to open a socket to use for connecting: %s\n",
219                strerror (errno));
220       return SOCKET_UNKNOWN;
221     }
222 
223   if (fcntl (se->fd, F_SETFL, O_NONBLOCK) < 0)
224     {
225       fprintf (stderr, "Warning: failed set socket %s nonblocking: %s\n",
226                se->name, strerror (errno));
227       return SOCKET_UNKNOWN;
228     }
229 
230 
231   memset (&saddr, '\0', sizeof (saddr)); /* nul-terminates the sun_path */
232 
233   saddr.sun_family = AF_UNIX;
234   strncpy (saddr.sun_path, se->name, sizeof (saddr.sun_path) - 1);
235 
236   do
237     {
238       ret = connect (se->fd, (struct sockaddr*) &saddr, sizeof (saddr));
239     }
240   while (ret < 0 && errno == EINTR);
241 
242   if (ret >= 0)
243     return SOCKET_ALIVE;
244   else
245     {
246       switch (errno)
247         {
248         case EINPROGRESS:
249         case EAGAIN:
250           return SOCKET_UNKNOWN;
251         case ECONNREFUSED:
252           return SOCKET_DEAD;
253         default:
254           fprintf (stderr, "Warning: unexpected error connecting to socket %s: %s\n",
255                    se->name, strerror (errno));
256           return SOCKET_FAILED_TO_HANDLE;
257         }
258     }
259 }
260 
261 static int
handle_sockets(SocketEntry ** entries,int n_entries)262 handle_sockets (SocketEntry **entries,
263                 int           n_entries)
264 {
265   int i;
266   int n_unknown;
267 
268   n_unknown = 0;
269 
270   i = 0;
271   while (i < n_entries)
272     {
273       SocketEntry *se;
274       SocketStatus status;
275 
276       se = entries[i];
277       ++i;
278 
279       if (se->fd >= 0)
280         {
281           fprintf (stderr, "Internal error, socket has fd  kept open while status = %d\n",
282                    se->status);
283           exit (1);
284         }
285 
286       if (se->status != SOCKET_UNKNOWN)
287         continue;
288 
289       status = open_socket (se);
290 
291       switch (status)
292         {
293         case SOCKET_DEAD:
294           cleaned_count += 1;
295           if (unlink (se->name) < 0)
296             {
297               fprintf (stderr, "Warning: Failed to delete %s: %s\n",
298                        se->name, strerror (errno));
299 
300               se->status = SOCKET_FAILED_TO_HANDLE;
301             }
302           else
303             se->status = SOCKET_UNLINKED;
304           break;
305 
306         case SOCKET_ALIVE:
307           alive_count += 1;
308           /* FALL THRU */
309 
310         case SOCKET_FAILED_TO_HANDLE:
311         case SOCKET_UNKNOWN:
312           se->status = status;
313           break;
314 
315         case SOCKET_UNLINKED:
316           fprintf (stderr, "Bad status from open_socket(), should not happen\n");
317           exit (1);
318           break;
319         }
320 
321       if (se->fd >= 0)
322         {
323           close (se->fd);
324           se->fd = -1;
325         }
326 
327       if (se->status == SOCKET_UNKNOWN)
328         n_unknown += 1;
329     }
330 
331   return n_unknown == 0;
332 }
333 
334 static void
clean_dir(const char * dir)335 clean_dir (const char *dir)
336 {
337   SocketEntry **entries;
338   int n_entries;
339 
340   read_sockets (dir, &entries, &n_entries);
341 
342   /* open_socket() will fail conclusively after
343    * several retries, so this loop is guaranteed
344    * to terminate eventually
345    */
346   while (!handle_sockets (entries, n_entries))
347     {
348       fprintf (stderr, "Unable to determine state of some sockets, retrying in 2 seconds\n");
349       sleep (2);
350     }
351 
352   unhandled_count += (n_entries - alive_count - cleaned_count);
353 }
354 
355 #endif /* AF_UNIX */
356 
357 static void
usage(int ecode)358 usage (int ecode)
359 {
360   fprintf (stderr, "dbus-cleanup-sockets [--version] [--help] <socketdir>\n");
361   exit (ecode);
362 }
363 
364 static void
version(void)365 version (void)
366 {
367   printf ("D-Bus Socket Cleanup Utility %s\n"
368           "Copyright (C) 2003 Red Hat, Inc.\n"
369           "Copyright (C) 2002 Michael Meeks\n"
370           "This is free software; see the source for copying conditions.\n"
371           "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
372           VERSION);
373   exit (0);
374 }
375 
376 int
main(int argc,char ** argv)377 main (int argc, char **argv)
378 {
379   int i;
380   int saw_doubledash;
381   const char *dirname;
382 
383   saw_doubledash = FALSE;
384   dirname = NULL;
385   i = 1;
386   while (i < argc)
387     {
388       const char *arg = argv[i];
389 
390       if (strcmp (arg, "--help") == 0 ||
391           strcmp (arg, "-h") == 0 ||
392           strcmp (arg, "-?") == 0)
393         usage (0);
394       else if (strcmp (arg, "--version") == 0)
395         version ();
396       else if (!saw_doubledash)
397 	{
398           if (strcmp (arg, "--") == 0)
399             saw_doubledash = TRUE;
400           else if (*arg == '-')
401             usage (1);
402 	}
403       else
404         {
405           if (dirname != NULL)
406             {
407               fprintf (stderr, "dbus-cleanup-sockets only supports a single directory name\n");
408               exit (1);
409             }
410 
411           dirname = arg;
412         }
413 
414       ++i;
415     }
416 
417   /* Default to session socket dir, usually /tmp */
418   if (dirname == NULL)
419     dirname = DBUS_SESSION_SOCKET_DIR;
420 
421 #ifdef AF_UNIX
422   clean_dir (dirname);
423 
424   printf ("Cleaned up %d sockets in %s; %d sockets are still in use; %d in unknown state\n",
425           cleaned_count, dirname, alive_count, unhandled_count);
426 #else
427   printf ("This system does not support UNIX domain sockets, so dbus-cleanup-sockets does nothing\n");
428 #endif
429 
430   return 0;
431 }
432