2007-07-14 02:44:01 +00:00
|
|
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
2003-06-01 07:33:59 +00:00
|
|
|
/* dbus-cleanup-sockets.c dbus-cleanup-sockets utility
|
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2003 Red Hat, Inc.
|
|
|
|
|
* Copyright (C) 2002 Michael Meeks
|
2022-05-18 14:55:48 +01:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
2003-06-01 07:33:59 +00:00
|
|
|
*
|
|
|
|
|
* Note that this file is NOT licensed under the Academic Free License,
|
|
|
|
|
* as it is based on linc-cleanup-sockets which is LGPL.
|
|
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program; if not, write to the Free Software
|
2009-07-10 19:32:38 -04:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2003-06-01 07:33:59 +00:00
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2018-08-03 15:21:53 +01:00
|
|
|
#include <dbus/dbus-macros.h>
|
|
|
|
|
|
2003-06-01 07:33:59 +00:00
|
|
|
#ifndef TRUE
|
|
|
|
|
#define TRUE (1)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef FALSE
|
|
|
|
|
#define FALSE (0)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef NULL
|
|
|
|
|
#define NULL ((void*) 0)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void*
|
|
|
|
|
xmalloc (size_t bytes)
|
|
|
|
|
{
|
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
|
|
if (bytes == 0)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
mem = malloc (bytes);
|
|
|
|
|
|
|
|
|
|
if (mem == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Allocation of %d bytes failed\n",
|
|
|
|
|
(int) bytes);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void*
|
|
|
|
|
xrealloc (void *old, size_t bytes)
|
|
|
|
|
{
|
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
|
|
if (bytes == 0)
|
|
|
|
|
{
|
|
|
|
|
free (old);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mem = realloc (old, bytes);
|
|
|
|
|
|
|
|
|
|
if (mem == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Reallocation of %d bytes failed\n",
|
|
|
|
|
(int) bytes);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef AF_UNIX
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
SOCKET_UNKNOWN,
|
|
|
|
|
SOCKET_FAILED_TO_HANDLE,
|
|
|
|
|
SOCKET_DEAD,
|
|
|
|
|
SOCKET_ALIVE,
|
|
|
|
|
SOCKET_UNLINKED
|
|
|
|
|
} SocketStatus;
|
|
|
|
|
|
|
|
|
|
static int alive_count = 0;
|
|
|
|
|
static int cleaned_count = 0;
|
|
|
|
|
static int unhandled_count = 0;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
char *name;
|
|
|
|
|
int fd;
|
|
|
|
|
SocketStatus status;
|
|
|
|
|
int n_retries;
|
|
|
|
|
} SocketEntry;
|
|
|
|
|
|
|
|
|
|
static SocketEntry*
|
|
|
|
|
socket_entry_new (const char *dir,
|
|
|
|
|
const char *fname)
|
|
|
|
|
{
|
|
|
|
|
SocketEntry *se;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
se = xmalloc (sizeof (SocketEntry));
|
|
|
|
|
|
|
|
|
|
len = strlen (dir) + strlen (fname) + 2; /* 2 = nul and '/' */
|
|
|
|
|
se->name = xmalloc (len);
|
|
|
|
|
|
|
|
|
|
strcpy (se->name, dir);
|
|
|
|
|
strcat (se->name, "/");
|
|
|
|
|
strcat (se->name, fname);
|
|
|
|
|
|
|
|
|
|
se->fd = -1;
|
|
|
|
|
|
|
|
|
|
se->status = SOCKET_UNKNOWN;
|
|
|
|
|
|
|
|
|
|
se->n_retries = 0;
|
|
|
|
|
|
|
|
|
|
return se;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
free_socket_entry (SocketEntry *se)
|
|
|
|
|
{
|
2011-01-17 12:18:40 +00:00
|
|
|
if (se)
|
|
|
|
|
{
|
|
|
|
|
free (se->name);
|
|
|
|
|
if (se->fd >= 0)
|
|
|
|
|
close (se->fd);
|
|
|
|
|
free (se);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
free_socket_entries (SocketEntry** entries,
|
|
|
|
|
int n_entries)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (entries)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < n_entries; ++i)
|
|
|
|
|
free_socket_entry (entries[i]);
|
|
|
|
|
free (entries);
|
|
|
|
|
}
|
2003-06-01 07:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
read_sockets (const char *dir,
|
|
|
|
|
SocketEntry ***entries_p,
|
|
|
|
|
int *n_entries_p)
|
|
|
|
|
{
|
|
|
|
|
DIR *dirh;
|
|
|
|
|
struct dirent *dent;
|
|
|
|
|
SocketEntry **entries;
|
|
|
|
|
int n_entries;
|
|
|
|
|
int allocated;
|
|
|
|
|
|
|
|
|
|
n_entries = 0;
|
|
|
|
|
allocated = 2;
|
|
|
|
|
entries = xmalloc (sizeof (SocketEntry*) * allocated);
|
|
|
|
|
|
|
|
|
|
dirh = opendir (dir);
|
|
|
|
|
if (dirh == NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Failed to open directory %s: %s\n",
|
|
|
|
|
dir, strerror (errno));
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((dent = readdir (dirh)))
|
|
|
|
|
{
|
|
|
|
|
SocketEntry *se;
|
|
|
|
|
|
|
|
|
|
if (strncmp (dent->d_name, "dbus-", 5) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
se = socket_entry_new (dir, dent->d_name);
|
|
|
|
|
|
|
|
|
|
if (n_entries == allocated)
|
|
|
|
|
{
|
|
|
|
|
allocated *= 2;
|
|
|
|
|
entries = xrealloc (entries, sizeof (SocketEntry*) * allocated);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries[n_entries] = se;
|
|
|
|
|
n_entries += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closedir (dirh);
|
|
|
|
|
|
|
|
|
|
*entries_p = entries;
|
|
|
|
|
*n_entries_p = n_entries;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static SocketStatus
|
|
|
|
|
open_socket (SocketEntry *se)
|
|
|
|
|
{
|
|
|
|
|
int ret;
|
|
|
|
|
struct sockaddr_un saddr;
|
|
|
|
|
|
|
|
|
|
if (se->n_retries > 5)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Warning: giving up on socket %s after several retries; unable to determine socket's status\n",
|
|
|
|
|
se->name);
|
|
|
|
|
return SOCKET_FAILED_TO_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
se->n_retries += 1;
|
|
|
|
|
|
|
|
|
|
se->fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
if (se->fd < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Warning: failed to open a socket to use for connecting: %s\n",
|
|
|
|
|
strerror (errno));
|
|
|
|
|
return SOCKET_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (fcntl (se->fd, F_SETFL, O_NONBLOCK) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Warning: failed set socket %s nonblocking: %s\n",
|
|
|
|
|
se->name, strerror (errno));
|
|
|
|
|
return SOCKET_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
memset (&saddr, '\0', sizeof (saddr)); /* nul-terminates the sun_path */
|
|
|
|
|
|
|
|
|
|
saddr.sun_family = AF_UNIX;
|
|
|
|
|
strncpy (saddr.sun_path, se->name, sizeof (saddr.sun_path) - 1);
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
ret = connect (se->fd, (struct sockaddr*) &saddr, sizeof (saddr));
|
|
|
|
|
}
|
|
|
|
|
while (ret < 0 && errno == EINTR);
|
|
|
|
|
|
|
|
|
|
if (ret >= 0)
|
|
|
|
|
return SOCKET_ALIVE;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
switch (errno)
|
|
|
|
|
{
|
|
|
|
|
case EINPROGRESS:
|
|
|
|
|
case EAGAIN:
|
|
|
|
|
return SOCKET_UNKNOWN;
|
|
|
|
|
case ECONNREFUSED:
|
|
|
|
|
return SOCKET_DEAD;
|
|
|
|
|
default:
|
|
|
|
|
fprintf (stderr, "Warning: unexpected error connecting to socket %s: %s\n",
|
|
|
|
|
se->name, strerror (errno));
|
|
|
|
|
return SOCKET_FAILED_TO_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
handle_sockets (SocketEntry **entries,
|
|
|
|
|
int n_entries)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int n_unknown;
|
|
|
|
|
|
|
|
|
|
n_unknown = 0;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
while (i < n_entries)
|
|
|
|
|
{
|
|
|
|
|
SocketEntry *se;
|
|
|
|
|
SocketStatus status;
|
|
|
|
|
|
|
|
|
|
se = entries[i];
|
|
|
|
|
++i;
|
|
|
|
|
|
|
|
|
|
if (se->fd >= 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Internal error, socket has fd kept open while status = %d\n",
|
|
|
|
|
se->status);
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (se->status != SOCKET_UNKNOWN)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
status = open_socket (se);
|
|
|
|
|
|
|
|
|
|
switch (status)
|
|
|
|
|
{
|
|
|
|
|
case SOCKET_DEAD:
|
|
|
|
|
cleaned_count += 1;
|
|
|
|
|
if (unlink (se->name) < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Warning: Failed to delete %s: %s\n",
|
|
|
|
|
se->name, strerror (errno));
|
|
|
|
|
|
|
|
|
|
se->status = SOCKET_FAILED_TO_HANDLE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
se->status = SOCKET_UNLINKED;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SOCKET_ALIVE:
|
|
|
|
|
alive_count += 1;
|
|
|
|
|
/* FALL THRU */
|
|
|
|
|
|
|
|
|
|
case SOCKET_FAILED_TO_HANDLE:
|
|
|
|
|
case SOCKET_UNKNOWN:
|
|
|
|
|
se->status = status;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case SOCKET_UNLINKED:
|
2017-01-17 20:41:17 +00:00
|
|
|
default:
|
2003-06-01 07:33:59 +00:00
|
|
|
fprintf (stderr, "Bad status from open_socket(), should not happen\n");
|
|
|
|
|
exit (1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (se->fd >= 0)
|
|
|
|
|
{
|
|
|
|
|
close (se->fd);
|
|
|
|
|
se->fd = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (se->status == SOCKET_UNKNOWN)
|
|
|
|
|
n_unknown += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n_unknown == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
clean_dir (const char *dir)
|
|
|
|
|
{
|
|
|
|
|
SocketEntry **entries;
|
|
|
|
|
int n_entries;
|
|
|
|
|
|
|
|
|
|
read_sockets (dir, &entries, &n_entries);
|
|
|
|
|
|
|
|
|
|
/* open_socket() will fail conclusively after
|
|
|
|
|
* several retries, so this loop is guaranteed
|
|
|
|
|
* to terminate eventually
|
|
|
|
|
*/
|
|
|
|
|
while (!handle_sockets (entries, n_entries))
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "Unable to determine state of some sockets, retrying in 2 seconds\n");
|
|
|
|
|
sleep (2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unhandled_count += (n_entries - alive_count - cleaned_count);
|
2011-01-17 12:18:40 +00:00
|
|
|
|
|
|
|
|
free_socket_entries (entries, n_entries);
|
2003-06-01 07:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* AF_UNIX */
|
|
|
|
|
|
2018-08-03 15:21:53 +01:00
|
|
|
static void usage (int ecode) _DBUS_GNUC_NORETURN;
|
2003-06-01 07:33:59 +00:00
|
|
|
static void
|
|
|
|
|
usage (int ecode)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "dbus-cleanup-sockets [--version] [--help] <socketdir>\n");
|
|
|
|
|
exit (ecode);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-03 15:21:53 +01:00
|
|
|
static void version (void) _DBUS_GNUC_NORETURN;
|
2003-06-01 07:33:59 +00:00
|
|
|
static void
|
|
|
|
|
version (void)
|
|
|
|
|
{
|
2006-08-03 20:34:36 +00:00
|
|
|
printf ("D-Bus Socket Cleanup Utility %s\n"
|
2003-06-01 07:33:59 +00:00
|
|
|
"Copyright (C) 2003 Red Hat, Inc.\n"
|
|
|
|
|
"Copyright (C) 2002 Michael Meeks\n"
|
|
|
|
|
"This is free software; see the source for copying conditions.\n"
|
|
|
|
|
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
|
|
|
|
|
VERSION);
|
|
|
|
|
exit (0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int saw_doubledash;
|
|
|
|
|
const char *dirname;
|
|
|
|
|
|
|
|
|
|
saw_doubledash = FALSE;
|
|
|
|
|
dirname = NULL;
|
|
|
|
|
i = 1;
|
|
|
|
|
while (i < argc)
|
|
|
|
|
{
|
|
|
|
|
const char *arg = argv[i];
|
|
|
|
|
|
|
|
|
|
if (strcmp (arg, "--help") == 0 ||
|
|
|
|
|
strcmp (arg, "-h") == 0 ||
|
|
|
|
|
strcmp (arg, "-?") == 0)
|
|
|
|
|
usage (0);
|
|
|
|
|
else if (strcmp (arg, "--version") == 0)
|
|
|
|
|
version ();
|
|
|
|
|
else if (!saw_doubledash)
|
|
|
|
|
{
|
|
|
|
|
if (strcmp (arg, "--") == 0)
|
|
|
|
|
saw_doubledash = TRUE;
|
|
|
|
|
else if (*arg == '-')
|
|
|
|
|
usage (1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (dirname != NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "dbus-cleanup-sockets only supports a single directory name\n");
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dirname = arg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Default to session socket dir, usually /tmp */
|
|
|
|
|
if (dirname == NULL)
|
|
|
|
|
dirname = DBUS_SESSION_SOCKET_DIR;
|
|
|
|
|
|
|
|
|
|
#ifdef AF_UNIX
|
|
|
|
|
clean_dir (dirname);
|
|
|
|
|
|
|
|
|
|
printf ("Cleaned up %d sockets in %s; %d sockets are still in use; %d in unknown state\n",
|
|
|
|
|
cleaned_count, dirname, alive_count, unhandled_count);
|
|
|
|
|
#else
|
|
|
|
|
printf ("This system does not support UNIX domain sockets, so dbus-cleanup-sockets does nothing\n");
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|