xrdp/sesman/lock_uds.c

151 lines
4.1 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) 2022 Matt Burt, all xrdp contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* @file lock_uds.c
* @brief Providing a locking facility for Unix Domain Sockets
* @author Matt Burt
*
* It is difficult for a server to determine whether a socket it wishes
* to listen on is already active or not. The purpose of this module is to
* provide a locking facility which can be used as a proxy for this.
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "arch.h"
#include "os_calls.h"
#include "string_calls.h"
#include "log.h"
#include "lock_uds.h"
struct lock_uds
{
char *filename; ///<< Name of the lock file
int fd; ///<< File descriptor for open file
int pid; ///<< PID of process originally taking out lock
};
/******************************************************************************/
struct lock_uds *
lock_uds(const char *sockname)
{
struct lock_uds *lock = NULL;
char *filename = NULL;
int fd = -1;
if (sockname == NULL || sockname[0] != '/')
{
LOG_DEVEL(LOG_LEVEL_ERROR, "Invalid sockname '%s'",
(sockname == NULL) ? "<null>" : sockname);
}
else
{
/* Allocate space for lock filename and result */
filename = (char *)g_malloc(g_strlen(sockname) + 1 + 5 + 1, 0);
lock = g_new0(struct lock_uds, 1);
if (lock == NULL || filename == NULL)
{
LOG(LOG_LEVEL_ERROR, "%s : Out of memory", __func__);
}
else
{
int saved_umask;
/* Construct the filename */
/* This call is guaranteed to succeed as we know that sockname
* contains at least one '/' */
char *p = filename;
const char *basename = g_strrchr(sockname, '/') + 1;
g_memcpy(p, sockname, basename - sockname);
p += basename - sockname;
*p++ = '.';
g_strcpy(p, basename);
p += g_strlen(p);
*p++ = '.';
*p++ = 'l';
*p++ = 'o';
*p++ = 'c';
*p++ = 'k';
*p++ = '\0';
saved_umask = g_umask_hex(0x77);
fd = g_file_open_rw(filename);
g_umask_hex(saved_umask);
if (fd < 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to create '%s' [%s]",
filename, g_get_strerror());
}
else if (g_file_lock(fd, 0, 0) == 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to get lock for '%s' - "
"program already running?", sockname);
g_file_close(fd);
fd = -1;
}
else
{
(void)g_file_set_cloexec(fd, 1);
}
}
}
if (fd >= 0)
{
/* Success - finish off */
lock->filename = filename;
lock->fd = fd;
lock->pid = g_getpid();
}
else
{
g_free(filename);
g_free(lock);
lock = NULL;
}
return lock;
}
/******************************************************************************/
void
unlock_uds(struct lock_uds *lock)
{
if (lock != NULL)
{
if (lock->fd >= 0)
{
g_file_close(lock->fd);
lock->fd = -1; // In case of use-after-free
}
/* Only delete the lock file if we are the process which
* created it */
if (g_getpid() == lock->pid)
{
g_file_delete(lock->filename);
}
g_free(lock->filename);
lock->filename = NULL;
g_free(lock);
}
}