164 lines
3.9 KiB
C
164 lines
3.9 KiB
C
/* ****************************************************************************
|
|
* comlist/builtin/linux.c -- find out Linux serial devices.
|
|
* Copyright (C) 2016-2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
|
|
*
|
|
* This file is part of libcasio.
|
|
* libcasio is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 3.0 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* libcasio 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 Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with libcasio; if not, see <http://www.gnu.org/licenses/>.
|
|
* ************************************************************************* */
|
|
#include "../builtin.h"
|
|
#ifndef LIBCASIO_DISABLED_LINUX_SERIAL
|
|
# include <sys/stat.h>
|
|
# include <dirent.h>
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
# include <errno.h>
|
|
|
|
typedef struct lincookie {
|
|
char *path;
|
|
char *devname;
|
|
size_t path_max;
|
|
DIR *dp;
|
|
char *start;
|
|
} lincookie_t;
|
|
|
|
/**
|
|
* next_serial_port:
|
|
* Get the next serial port.
|
|
*
|
|
* @arg cookie the lincookie.
|
|
* @arg ptr the pointer to the next path.
|
|
* @return the error code (0 if ok).
|
|
*/
|
|
|
|
CASIO_LOCAL int CASIO_EXPORT next_serial_port(lincookie_t *cookie,
|
|
char const **ptr)
|
|
{
|
|
struct dirent *dr;
|
|
struct stat st;
|
|
ssize_t rl;
|
|
char *res;
|
|
|
|
while (1) {
|
|
/* Get the next entry. */
|
|
|
|
if (!(dr = readdir(cookie->dp)))
|
|
return (casio_error_iter);
|
|
|
|
/* Copy that into the path and check the type. */
|
|
|
|
strcpy(cookie->start, dr->d_name);
|
|
if (lstat(cookie->path, &st) || (st.st_mode & S_IFMT) != S_IFLNK)
|
|
continue;
|
|
|
|
/* Get the destination path. */
|
|
|
|
rl = readlink(cookie->path, cookie->devname, PATH_MAX + 1);
|
|
if (rl < 0)
|
|
continue;
|
|
cookie->devname[rl] = 0;
|
|
|
|
/* If the path is a relative one, adapt it. */
|
|
|
|
if (cookie->devname[0] != '/') {
|
|
strcpy(cookie->start, cookie->devname);
|
|
if (!(res = realpath(cookie->path, cookie->devname)))
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
*ptr = cookie->devname;
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
* end_iter:
|
|
* End the iterator by freeing all related allocated resources.
|
|
*
|
|
* @arg cookie the cookie.
|
|
*/
|
|
|
|
CASIO_LOCAL void CASIO_EXPORT end_iter(lincookie_t *cookie)
|
|
{
|
|
closedir(cookie->dp);
|
|
casio_free(cookie);
|
|
}
|
|
|
|
/* Functions. */
|
|
|
|
CASIO_LOCAL casio_iter_funcs_t linux_serial_functions = {
|
|
(casio_next_t *)&next_serial_port,
|
|
NULL,
|
|
(casio_end_t *)&end_iter
|
|
};
|
|
|
|
/**
|
|
* casio_iter_linux_serial:
|
|
* List serial ports under Linux.
|
|
*
|
|
* Links in the /dev/serial/by-id/ should resolve to relative paths, but
|
|
* I also managed absolute paths, in case.
|
|
*
|
|
* @arg iterp the iterator to create.
|
|
* @return the error.
|
|
*/
|
|
|
|
int CASIO_EXPORT casio_iter_linux_serial(casio_iter_t **iterp)
|
|
{
|
|
lincookie_t *cookie;
|
|
size_t path_max;
|
|
|
|
/* Get the maximum path size.
|
|
* Snippet found in `man 3 realpath`, glibc version. */
|
|
|
|
#ifdef PATH_MAX
|
|
path_max = PATH_MAX;
|
|
#else
|
|
path_max = pathconf(path, _PC_PATH_MAX);
|
|
if (path_max <= 0)
|
|
path_max = 4096;
|
|
#endif
|
|
++path_max;
|
|
|
|
/* Create the cookie. */
|
|
|
|
if (!(cookie = casio_alloc(sizeof(*cookie) + path_max + path_max, 1)))
|
|
return (casio_error_alloc);
|
|
|
|
cookie->path = (char *)(void *)&cookie[1];
|
|
cookie->devname = cookie->path + path_max;
|
|
|
|
/* Open the directory stream. */
|
|
|
|
strcpy(cookie->path, "/dev/serial/by-id/");
|
|
if (!(cookie->dp = opendir(cookie->path))) {
|
|
casio_free(cookie);
|
|
|
|
/* TODO: check errno */
|
|
return (casio_error_unknown);
|
|
}
|
|
|
|
/* Prepare what's left to prepare in the cookie. */
|
|
|
|
cookie->start = strchr(cookie->path, '\0');
|
|
cookie->path_max = path_max;
|
|
|
|
/* Create the iterator. */
|
|
|
|
return (casio_iter(iterp, cookie, linux_serial_functions));
|
|
}
|
|
|
|
# endif /* __linux__ */
|