Compare commits

...
This repository has been archived on 2024-06-12. You can view files and clone it, but cannot push or open issues or pull requests.

5 Commits
master ... dev

Author SHA1 Message Date
Alexander Zhirov 089154cb17 asd 2023-12-26 01:03:30 +03:00
Alexander Zhirov ee679a128b - 2023-12-25 17:06:59 +03:00
Alexander Zhirov cd5f9f7a12 - 2023-12-25 00:06:56 +03:00
Alexander Zhirov bb72f57776 Added udev code 2023-12-24 02:33:54 +03:00
Alexander Zhirov 78e8b7e491 The code is rewritten from scratch, based on udev 2023-12-24 00:35:41 +03:00
22 changed files with 761 additions and 434 deletions

24
.vscode/settings.json vendored
View File

@ -19,7 +19,29 @@
"cstdint": "c",
"fcntl.h": "c",
"file.h": "c",
"dir.h": "c"
"dir.h": "c",
"mportlink-core.h": "c",
"mportlink.h": "c",
"mportlink-lib.h": "c",
"errno.h": "c",
"stdlib.h": "c",
"string.h": "c",
"libudev.h": "c",
"mpl-lib.h": "c",
"mpl-core.h": "c",
"mpl.h": "c",
"mpl-udev.h": "c",
"mpl-args.h": "c",
"*.in": "cpp",
"signal.h": "c",
"stdio.h": "c",
"getopt.h": "c",
"mpl-common.h": "c",
"cerrno": "c",
"string": "c",
"string_view": "c",
"regex.h": "c",
"dirent.h": "c"
},
"cmake.configureOnOpen": false
}

2
.vscode/tasks.json vendored
View File

@ -8,7 +8,7 @@
"-fdiagnostics-color=always",
"-g",
"*.c",
"`pkg-config", "--libs", "--cflags", "mm-glib`",
"`pkg-config", "--libs", "udev`",
"-o",
"${workspaceFolder}/bin/mportlink"
],

View File

@ -1,6 +1,10 @@
# Changelog
## [0.2.0](https://github.com/AlexanderZhirov/MPortLink/compare/0.1.0...v0.2.0)
## [Unreleased]
### Changed
- The code is rewritten from scratch, based on `udev`
## [0.2.0](https://github.com/AlexanderZhirov/MPortLink/compare/0.1.0...0.2.0)
### Changed
- Creation of links to ports has been moved to the directory `/dev/dongle`

View File

@ -1,15 +1,42 @@
cmake_minimum_required(VERSION 3.18.4)
project(mportlink)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Os")
set(VERSION_MAJOR 0)
set(VERSION_MINOR 1)
set(VERSION_PATCH 0)
string(TIMESTAMP CURRENT_YEAR "%y" UTC)
string(TIMESTAMP DAY_OF_YEAR "%j" UTC)
string(CONCAT BASEVERSION "${VERSION_MAJOR}" "." "${VERSION_MINOR}" "." "${VERSION_PATCH}")
string(CONCAT BUILDVERSION "${BASEVERSION}" "." "${CURRENT_YEAR}" "${DAY_OF_YEAR}")
execute_process(
COMMAND git describe HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
)
if(GIT_SHORT_HASH STREQUAL "")
set(VERSION "${BUILDVERSION}")
else()
set(VERSION "${GIT_VERSION}")
endif()
add_definitions(-DVERSION="${VERSION}")
file(GLOB_RECURSE SRC_FILES_LIST src/*.c)
find_package(PkgConfig REQUIRED)
pkg_search_module(MM REQUIRED mm-glib)
pkg_search_module(UDEV REQUIRED libudev)
include_directories(${MM_INCLUDE_DIRS})
link_directories(${MM_LIBRARY_DIRS})
add_definitions(${MM_CFLAGS_OTHER})
include_directories(${UDEV_INCLUDE_DIRS})
link_directories(${UDEV_LIBRARY_DIRS})
add_definitions(${UDEV_CFLAGS_OTHER})
add_executable(${PROJECT_NAME} ${SRC_FILES_LIST})
target_link_libraries(${PROJECT_NAME} ${MM_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${UDEV_LIBRARIES})

View File

@ -1,42 +1,24 @@
# MPortLink
[![license](https://img.shields.io/github/license/AlexanderZhirov/mportlink.svg?sort=semver&style=for-the-badge&color=green)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.html)
[![main](https://img.shields.io/badge/dynamic/json.svg?label=git.zhirov.kz&style=for-the-badge&url=https://git.zhirov.kz/api/v1/repos/alexander/MPortLink/tags&query=$[0].name&color=violet&logo=C)](https://git.zhirov.kz/alexander/MPortLink)
[![githab](https://img.shields.io/github/v/tag/AlexanderZhirov/mportlink.svg?sort=semver&style=for-the-badge&color=blue&label=github&logo=C)](https://github.com/AlexanderZhirov/mportlink)
[![linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)](https://www.linux.org/)
**MPortLink** (*modem port link*) is designed to track connected USB modems and create permanent symbolic links to ports.
MPortLink was developed for direct use of several USB modems when connected to Asterisk telephony, the ports of which were randomly determined by the operating system. The utility allows you to create permanent symbolic links to devices, regardless of the order in which the ports of the USB modem device were defined.
Based on [ModemManager](https://gitlab.freedesktop.org/mobile-broadband/ModemManager).
## Dependency
The installed `modemmanager` is required for use.
## Build
`libmm-glib` library is required for the build. It is called differently in different distributions, for example: `libmm-glib-dev` or `libmm-glib-devel`.
After installing the dependencies, you just need to run the script `build.sh `and `mportlink` will be built in the `build` project directory.
Just need to run the script `build.sh `and `mportlink` will be built in the `build` project directory.
### Manually
```
gcc -Werror -Wall -Os src/*.c `pkg-config --libs --cflags mm-glib` -o mportlink
gcc -Werror -Wall -Os src/*.c `pkg-config --libs libudev` -o mportlink
```
## Using
Superuser rights are required to work. Start the daemon and connect your USB modem. In `journalctl` you will see his work. Links to the ports of the connected modem will be created in the `/dev/dongle` directory.
```
Dec 21 13:51:02 solus mportlink[17764]: main: starting the mportlink daemon
Dec 21 13:51:26 solus mportlink[17764]: mpl_symlink_ports, dst_path: the symbolic link has been successfully created [/dev/dongle/358**********26-0 -> /dev/ttyUSB0]
Dec 21 13:51:26 solus mportlink[17764]: mpl_symlink_ports, dst_path: the symbolic link has been successfully created [/dev/dongle/358**********26-1 -> /dev/ttyUSB1]
Dec 21 13:51:41 solus mportlink[17764]: mpl_unlink_ports, dst_path: link was successfully deleted [/dev/dongle/358**********26-0]
Dec 21 13:51:41 solus mportlink[17764]: mpl_unlink_ports, dst_path: link was successfully deleted [/dev/dongle/358**********26-1]
Dec 21 13:51:48 solus mportlink[17764]: signals_handler: cancelling the operation...
Dec 21 13:51:48 solus mportlink[17764]: signals_handler: cancelling the main loop...
Dec 21 13:51:48 solus mportlink[17764]: main: stopping the mportlink daemon
```
## To-Do
Currently, the utility is linked to the ModemManager server, through which the connected modems are identified. It is planned to disconnect from this server, as there is a problem with the capture of the device and the unavailability of using Asterisk.

BIN
mportlink Executable file

Binary file not shown.

View File

@ -1,6 +1,6 @@
[Unit]
Description=Modem Port Link
After=ModemManager.service
After=
[Service]
ExecStart=/usr/bin/mportlink

View File

@ -1,41 +0,0 @@
#include "mportlink.h"
MMManager *mmcli_get_manager_finish(GAsyncResult *res)
{
return g_task_propagate_pointer(G_TASK(res), NULL);
}
void manager_new_ready(GDBusConnection *connection, GAsyncResult *res, GTask *task)
{
MMManager *manager;
gchar *name_owner;
GError *error = NULL;
manager = mm_manager_new_finish(res, &error);
if (!manager)
{
syslog(LOG_ERR, "manager_new_ready, manager: couldn't create manager: %s\n", error ? error->message : "unknown error");
exit(EXIT_FAILURE);
}
name_owner = g_dbus_object_manager_client_get_name_owner(G_DBUS_OBJECT_MANAGER_CLIENT(manager));
if (!name_owner)
{
syslog(LOG_ERR, "manager_new_ready, name_owner: couldn't find the ModemManager process in the bus");
exit(EXIT_FAILURE);
}
g_free(name_owner);
g_task_return_pointer(task, manager, g_object_unref);
g_object_unref(task);
}
void mmcli_get_manager(GDBusConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
{
GTask *task;
task = g_task_new(connection, cancellable, callback, user_data);
mm_manager_new(connection, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, cancellable, (GAsyncReadyCallback)manager_new_ready, task);
}

View File

@ -1,11 +0,0 @@
#include <libmm-glib.h>
#include <syslog.h>
typedef struct
{
MMManager *manager;
GCancellable *cancellable;
} Context;
MMManager *mmcli_get_manager_finish(GAsyncResult *res);
void mmcli_get_manager(GDBusConnection *connection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data);

111
src/mpl-common.c Normal file
View File

@ -0,0 +1,111 @@
/*
* mpl-common.c
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#include "mpl-common.h"
#include <sys/stat.h>
#include <sys/file.h>
#define LOCKFILE "/run/lock/mportlink.lock"
static int mpl_create_dongle_dir()
{
struct stat st;
if (stat(MPL_DONGLE_PATH, &st) == 0)
{
if (!S_ISDIR(st.st_mode))
{
MPLOG(LOG_WARNING, "%s exists and is not a directory", MPL_DONGLE_PATH);
return 1;
}
}
else if (mkdir(MPL_DONGLE_PATH, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
MPLOG(LOG_ERR, "unable to create a directory %s", MPL_DONGLE_PATH);
return 1;
}
return 0;
}
void static mpl_free(MPL *mpl)
{
if (mpl)
free(mpl);
}
MPL *mpl_init(MPL_PARAMETERS *parameters)
{
MPL *mpl = (MPL *)calloc(1, sizeof(MPL));
if (!mpl)
{
MPLOG(LOG_ERR, "error getting the MPL structure");
exit(1);
}
mpl->parameters.check_device = parameters->check_device;
mpl->cancellable.signal = 0;
mpl->cancellable.exit = 0;
mpl->fd = open(LOCKFILE, O_CREAT | O_RDWR, 0644);
if (mpl->fd < 0)
{
MPLOG(LOG_ERR, "unable to create a lockfile");
exit(1);
}
int rc = flock(mpl->fd, LOCK_EX | LOCK_NB);
if (rc || errno == EWOULDBLOCK)
{
MPLOG(LOG_ERR, "only one instance of %s is allowed", MPL_NAME);
exit(1);
}
if (mpl_create_dongle_dir())
{
MPLOG(LOG_ERR, "%s will be stopped", MPL_NAME);
exit(1);
}
if (mpl_udev_init(&mpl->udev))
{
MPLOG(LOG_ERR, "%s will be stopped", MPL_NAME);
mpl_free(mpl);
exit(1);
}
MPLOG(LOG_NOTICE, "starting the %s daemon", MPL_NAME);
return mpl;
}
void mpl_stop(MPL *mpl)
{
mpl_udev_free(&mpl->udev);
if (mpl->cancellable.exit)
{
flock(mpl->fd, LOCK_UN);
MPLOG(LOG_NOTICE, "stopping the %s daemon", MPL_NAME);
}
else
MPLOG(LOG_NOTICE, "successful completion of the process %d", getpid());
mpl_free(mpl);
closelog();
}
void mpl_loop(MPL *mpl)
{
if (mpl->parameters.check_device)
mpl_udev_check_devices(&mpl->udev);
mpl_udev_loop(&mpl->udev, &mpl->cancellable);
}

31
src/mpl-common.h Normal file
View File

@ -0,0 +1,31 @@
/*
* mpl-common.h
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#ifndef MPL_COMMON_H_
#define MPL_COMMON_H_
#include "mpl-lib.h"
#include "mpl-udev.h"
typedef struct
{
int check_device;
} MPL_PARAMETERS;
typedef struct
{
int fd;
MPL_PARAMETERS parameters;
MPL_CANCELLABLE cancellable;
MPL_UDEV udev;
} MPL;
MPL *mpl_init(MPL_PARAMETERS *parameters);
void mpl_stop(MPL *mpl);
void mpl_loop(MPL *mpl);
#endif

87
src/mpl-core.c Normal file
View File

@ -0,0 +1,87 @@
/*
* mpl-core.c
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#include "mpl-lib.h"
#include "mpl-core.h"
#include <regex.h>
#include <dirent.h>
#include <fcntl.h>
int device_path(const MPL_MODEM *modem)
{
DIR *dir;
struct dirent *ent;
regex_t regex;
const char *pattern = "^ttyUSB[0-9]*$";
int ret = regcomp(&regex, pattern, REG_EXTENDED);
if (ret != 0)
{
printf("Failed to compile regular expression\n");
return 1;
}
dir = opendir(modem->path);
if (dir == NULL)
{
printf("Failed to open directory: %s\n", modem->path);
regfree(&regex);
return 1;
}
while ((ent = readdir(dir)) != NULL)
{
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
{
continue;
}
ret = regexec(&regex, ent->d_name, 0, NULL, 0);
if (ret == 0)
{
printf("Found a file matching the pattern: %s\n", ent->d_name);
char *path = (char *)calloc(strlen(MPL_DEV_PATH) + strlen(ent->d_name) + 1, sizeof(char));
strcpy(path, MPL_DEV_PATH);
strcat(path, ent->d_name);
int fd = open(path, O_RDWR);
if (fd < 0)
{
printf("Не удалось открыть USB устройство.\n");
free(path);
continue;
}
char *tty = ttyname(fd);
close(fd);
if (tty != NULL)
{
printf("%s устройство tty: %s\n", modem->action ? "Добавлено" : "Удалено", tty);
// if (!num)
// print_imei(tty);
}
else
{
printf("Устройство tty не найдено\n");
}
}
else if (ret != REG_NOMATCH)
{
printf("Failed to apply regular expression\n");
}
}
regfree(&regex);
closedir(dir);
return 0;
}

20
src/mpl-core.h Normal file
View File

@ -0,0 +1,20 @@
/*
* mpl-core.h
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#ifndef MPL_CORE_H_
#define MPL_CORE_H_
typedef struct
{
int port;
char *path;
int action;
} MPL_MODEM;
int device_path(const MPL_MODEM *modem);
#endif

25
src/mpl-lib.c Normal file
View File

@ -0,0 +1,25 @@
/*
* mpl-lib.c
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#include "mpl-lib.h"
char *mpl_get_copy_string(const char *str)
{
int size = strlen(str);
char *copy = (char *)calloc(size + 1, sizeof(char));
if (!copy)
{
MPLOG(LOG_ERR, "error getting the string");
return NULL;
}
strcpy(copy, str);
copy[size] = '\0';
return copy;
}

39
src/mpl-lib.h Normal file
View File

@ -0,0 +1,39 @@
/*
* mpl-lib.h
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#ifndef MPL_LIB_H_
#define MPL_LIB_H_
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#define MPL_NAME "mportlink"
#define MPL_DEV_PATH "/dev/"
#define MPL_DONGLE_PATH (MPL_DEV_PATH "dongle/")
#define MPLOG(PRIORITY, fmt, ...) do { \
char buf[256]; \
snprintf(buf, sizeof(buf), fmt __VA_OPT__(,) __VA_ARGS__); \
PRIORITY < LOG_WARNING && errno ? \
syslog(PRIORITY, "%s(): %s (%s)", __func__, buf, strerror(errno)) : \
syslog(PRIORITY, "%s(): %s", __func__, buf); \
} while(0)
typedef struct
{
int signal;
int exit;
} MPL_CANCELLABLE;
char *mpl_get_copy_string(const char *str);
#endif

259
src/mpl-udev.c Normal file
View File

@ -0,0 +1,259 @@
/*
* mpl-udev.c
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#include "mpl-udev.h"
#include "mpl-core.h"
#include <unistd.h>
#include <sys/wait.h>
void test(struct udev_device *dev)
{
const char *action = udev_device_get_action(dev);
// printf("action: %s\n", action);
// const char *devpath = udev_device_get_devpath(dev);
// printf("devpath: %s\n", devpath);
// const char *subsystem = udev_device_get_subsystem(dev);
// printf("subsystem: %s\n", subsystem);
const char *devtype = udev_device_get_devtype(dev);
// printf("devtype: %s\n", devtype);
const char *driver = udev_device_get_driver(dev);
// printf("driver: %s\n", driver);
// const char *sysattr_value = udev_device_get_sysattr_value(dev, "bInterfaceNumber");
// printf("Номер порта: %s\n", sysattr_value);
const char *syspath = udev_device_get_syspath(dev);
// printf("syspath: %s\n", syspath);
// const char *sysname = udev_device_get_sysname(dev);
// printf("sysname: %s\n", sysname);
// const char *sysnum = udev_device_get_sysnum(dev);
// printf("sysnum: %s\n", sysnum);
const char *devnode = udev_device_get_devnode(dev);
// printf("devnode: %s\n", devnode);
// int is_initialized = udev_device_get_is_initialized(dev);
// printf("is_initialized: %d\n", is_initialized);
printf("action: %s\tdevtype: %s\tdriver: %s\tsyspath: %s\tdevnode: %s\n", action, devtype, driver, syspath, devnode);
}
int mpl_udev_init(MPL_UDEV *udev)
{
udev->context = udev_new();
if (udev->context == NULL)
{
MPLOG(LOG_ERR, "error creating udev context");
return 1;
}
udev->monitor = udev_monitor_new_from_netlink(udev->context, "udev");
if (udev->monitor == NULL)
{
MPLOG(LOG_ERR, "error creating udev monitor");
mpl_udev_free(udev);
return 1;
}
udev_monitor_filter_add_match_subsystem_devtype(udev->monitor, "usb", NULL);
udev_monitor_enable_receiving(udev->monitor);
udev->fd = udev_monitor_get_fd(udev->monitor);
if (udev->fd < 0)
{
MPLOG(LOG_ERR, "error creating udev monitor descriptor");
mpl_udev_free(udev);
return 1;
}
return 0;
}
// action: bind devtype: usb_interface driver: option syspath: /sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.3/3-3.3:1.1
// action: unbind devtype: usb_interface driver: (null) syspath: /sys/devices/pci0000:00/0000:00:14.0/usb3/3-3/3-3.3/3-3.3:1.1
static int mpl_device_is_interface(struct udev_device *dev)
{
return strcmp(udev_device_get_devtype(dev), "usb_interface") == 0;
}
static int mpl_device_is_modem(struct udev_device *dev)
{
const char *driver = udev_device_get_driver(dev);
const char *sysattr = udev_device_get_sysattr_value(dev, "bInterfaceNumber");
if (!driver || !sysattr)
return 0;
return mpl_device_is_interface(dev) && strcmp(driver, "option") == 0 && sysattr;
}
static int mpl_connect_modem(struct udev_device *dev)
{
const char *action = udev_device_get_property_value(dev, "ACTION");
return strcmp(action, "bind") == 0 && mpl_device_is_modem(dev);
}
static int mpl_disconnect_modem(struct udev_device *dev)
{
const char *action = udev_device_get_property_value(dev, "ACTION");
return strcmp(action, "unbind") == 0 && mpl_device_is_interface(dev);
}
static MPL_MODEM *mpl_get_modem_info(struct udev_device *dev)
{
MPL_MODEM *modem = (MPL_MODEM *)calloc(1, sizeof(MPL_MODEM));
if (modem == NULL)
{
MPLOG(LOG_ERR, "error creating modem");
return NULL;
}
modem->port = atoi(udev_device_get_sysattr_value(dev, "bInterfaceNumber"));
modem->path = mpl_get_copy_string(udev_device_get_syspath(dev));
modem->action = strcmp(udev_device_get_property_value(dev, "ACTION"), "bind") == 0;
return modem;
}
static void mpl_free_modem(MPL_MODEM *modem)
{
if (modem->path)
free(modem->path);
free(modem);
}
int mpl_udev_check_devices(MPL_UDEV *udev)
{
struct udev_device *dev;
struct udev_enumerate *enumerate;
struct udev_list_entry *devices, *dev_list_entry;
MPL_MODEM *modem = NULL;
enumerate = udev_enumerate_new(udev->context);
if (enumerate == NULL)
{
MPLOG(LOG_ERR, "error creating udev enumerate");
return 1;
}
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_scan_devices(enumerate);
devices = udev_enumerate_get_list_entry(enumerate);
if (devices == NULL)
{
MPLOG(LOG_ERR, "error get udev devices");
return 1;
}
udev_list_entry_foreach(dev_list_entry, devices)
{
const char *syspath = udev_list_entry_get_name(dev_list_entry);
dev = udev_device_new_from_syspath(udev->context, syspath);
if (!mpl_device_is_modem(dev))
continue;
modem = mpl_get_modem_info(dev);
device_path(modem);
mpl_free_modem(modem);
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate);
return 0;
}
void mpl_udev_free(MPL_UDEV *udev)
{
if (udev->monitor)
{
udev_monitor_unref(udev->monitor);
udev->monitor = NULL;
}
if (udev->context)
{
udev_unref(udev->context);
udev->context = NULL;
}
}
void mpl_udev_loop(MPL_UDEV *udev, MPL_CANCELLABLE *cancellable)
{
MPL_MODEM *modem = NULL;
int status = -1;
pid_t pid = -1;
int stop = 0;
while (!stop)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(udev->fd, &fds);
if (select(udev->fd + 1, &fds, NULL, NULL, NULL) <= 0)
{
if (cancellable->exit)
return;
MPLOG(LOG_ERR, "file selection error");
break;
}
struct udev_device *dev = udev_monitor_receive_device(udev->monitor);
if (dev)
{
// test(dev);
if (mpl_connect_modem(dev))
{
modem = mpl_get_modem_info(dev);
pid = fork();
if (pid == 0)
{
stop = 1;
}
else
wait(&status);
}
else if (mpl_disconnect_modem(dev))
{
// unmount ports
}
udev_device_unref(dev);
}
}
mpl_udev_free(udev);
if (cancellable->exit)
return;
// printf("path: %s\t port: %d\t action: %d\n", modem->path, modem->port, modem->action);
device_path(modem);
mpl_free_modem(modem);
}

26
src/mpl-udev.h Normal file
View File

@ -0,0 +1,26 @@
/*
* mpl-udev.h
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#ifndef MPL_UDEV_H_
#define MPL_UDEV_H_
#include <libudev.h>
#include "mpl-lib.h"
typedef struct
{
struct udev *context;
struct udev_monitor *monitor;
int fd;
} MPL_UDEV;
int mpl_udev_init(MPL_UDEV *udev);
void mpl_udev_free(MPL_UDEV *udev);
int mpl_udev_check_devices(MPL_UDEV *udev);
void mpl_udev_loop(MPL_UDEV *udev, MPL_CANCELLABLE *cancellable);
#endif

77
src/mpl.c Normal file
View File

@ -0,0 +1,77 @@
/*
* mpl.c
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#include "mpl.h"
#include <getopt.h>
static MPL *mpl;
const char *const short_options = "hcv";
const struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"check-devices", 0, NULL, 'c'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0}};
[[noreturn]] void static print_usage_and_exit(int code)
{
printf("Usage: %s [OPTION]...\n\n", MPL_NAME);
puts(" -c, --check-devices checking connected devices at startup");
puts(" -h, --help display this help text and exit");
puts(" -v, --version display version information and exit\n");
exit(code);
}
[[noreturn]] void static print_version_and_exit()
{
printf("%s version %s\n", MPL_NAME, VERSION);
exit(0);
}
static void signals_handler(int signum)
{
MPLOG(LOG_WARNING, "cancelling the mportlink loop (%d)", signum);
mpl->cancellable.signal = signum;
mpl->cancellable.exit = 1;
}
int main(int argc, char *argv[])
{
int c;
MPL_PARAMETERS parameters =
{
.check_device = 0
};
while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) != -1)
switch (c)
{
case 'c':
parameters.check_device = 1;
break;
case 'v':
print_version_and_exit();
case 'h':
print_usage_and_exit(0);
case '?':
print_usage_and_exit(1);
}
mpl = mpl_init(&parameters);
signal(SIGHUP, signals_handler);
signal(SIGINT, signals_handler);
signal(SIGTERM, signals_handler);
mpl_loop(mpl);
mpl_stop(mpl);
return 0;
}

17
src/mpl.h Normal file
View File

@ -0,0 +1,17 @@
/*
* mpl.h
*
* by Alexander Zhirov (alexander@zhirov.kz)
* Telegram @alexanderzhirov
*/
#ifndef MPL_H_
#define MPL_H_
#ifndef VERSION
#define VERSION "unrelease"
#endif
#include "mpl-common.h"
#endif

View File

@ -1,253 +0,0 @@
#include "mportlink.h"
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#define DEV_PATH "/dev/"
#define DONGLE_PATH "/dev/dongle/"
typedef struct
{
const gchar *manufacturer;
const gchar *model;
const gchar *imei;
const gchar *device_identifier;
MMModemPortInfo *ports;
guint n_ports;
} MPLmodem;
void mpl_create_dongle_dir()
{
struct stat st;
if (stat(DONGLE_PATH, &st) == 0)
{
if (!S_ISDIR(st.st_mode))
{
syslog(LOG_ERR, "mpl_create_dongle_dir: %s exists and is not a directory", DONGLE_PATH);
raise(SIGINT);
}
}
else if (mkdir(DONGLE_PATH, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
{
syslog(LOG_ERR, "mpl_create_dongle_dir: unable to create a directory %s", DONGLE_PATH);
raise(SIGINT);
}
}
MPLmodem *mpl_get_new_modem()
{
MPLmodem *modem = (MPLmodem *)calloc(1, sizeof(MPLmodem));
if (modem == NULL)
{
syslog(LOG_ERR, "mpl_get_new_modem, modem: memory allocation error");
raise(SIGINT);
}
return modem;
}
void mpl_free_modem(MPLmodem *modem)
{
if (modem->n_ports && modem->ports)
mm_modem_port_info_array_free(modem->ports, modem->n_ports);
free(modem);
}
void mpl_print_modem_info(MPLmodem *modem, bool connected)
{
gchar *status;
status = connected ? "Connected" : "Disconnected";
g_print("%s %s %s (%s)\n", status, modem->manufacturer, modem->model, modem->device_identifier);
g_print("`-- Count ports: %d\n", modem->n_ports);
for (guint i = 0; i < modem->n_ports; i++)
{
if (i + 1 < modem->n_ports)
{
g_print(" |-- Port %d:\n", i);
g_print(" | |-- Name: %s\n", modem->ports[i].name);
g_print(" | `-- Link: %s-%d\n", modem->imei, i);
}
else
{
g_print(" `-- Port %d:\n", i);
g_print(" |-- Name: %s\n", modem->ports[i].name);
g_print(" `-- Link: %s-%d\n", modem->imei, i);
}
}
}
MPLmodem *mpl_get_new_modem_info(MMObject *obj)
{
MPLmodem *mpl_modem = NULL;
MMModem *mm_modem = mm_object_get_modem(obj);
if (mm_modem == NULL)
{
syslog(LOG_ERR, "mpl_get_new_modem_info, mm_modem: modem does not implement the interface");
return NULL;
}
mpl_modem = mpl_get_new_modem();
mpl_modem->manufacturer = mm_modem_get_manufacturer(mm_modem);
mpl_modem->model = mm_modem_get_model(mm_modem);
mpl_modem->imei = mm_modem_get_equipment_identifier(mm_modem);
mpl_modem->device_identifier = mm_modem_get_device_identifier(mm_modem);
gboolean success = mm_modem_get_ports(mm_modem, &mpl_modem->ports, &mpl_modem->n_ports);
g_object_unref(mm_modem);
if (success)
return mpl_modem;
else
syslog(LOG_WARNING, "mpl_get_new_modem_info, success: error receiving modem ports");
mpl_free_modem(mpl_modem);
return NULL;
}
char *mpl_get_dst_path(guint index, const gchar *imei)
{
char *dst_path = NULL;
guint size_id = snprintf(NULL, 0, "-%d", index);
char *id = (char *)calloc(size_id + 1, sizeof(char));
if (id == NULL)
{
syslog(LOG_ERR, "mpl_get_dst_path, id: memory allocation error");
raise(SIGINT);
}
sprintf(id, "-%d", index);
guint dst_path_size = strlen(DONGLE_PATH) + strlen(imei) + size_id;
dst_path = (char *)calloc(dst_path_size + 1, sizeof(char));
if (dst_path == NULL)
{
syslog(LOG_ERR, "mpl_get_dst_path, dst_path: memory allocation error");
raise(SIGINT);
}
strcpy(dst_path, DONGLE_PATH);
strcat(dst_path, imei);
strcat(dst_path, id);
free(id);
return dst_path;
}
char *mpl_get_src_path(const gchar *port_name)
{
char *src_path = NULL;
guint src_path_size = strlen(DEV_PATH) + strlen(port_name);
src_path = (char *)calloc(src_path_size + 1, sizeof(char));
if (src_path == NULL)
{
syslog(LOG_ERR, "mpl_get_src_path, src_path: memory allocation error");
raise(SIGINT);
}
strcpy(src_path, DEV_PATH);
strcat(src_path, port_name);
return src_path;
}
void mpl_symlink_ports(MPLmodem *modem)
{
for (guint i = 0; i < modem->n_ports; i++)
{
char *src_path = mpl_get_src_path(modem->ports[i].name);
char *dst_path = mpl_get_dst_path(i, modem->imei);
struct stat st;
if (access(src_path, F_OK) == -1)
{
syslog(LOG_ERR, "mpl_symlink_ports, src_path: device file is not available [%s]", src_path);
free(src_path);
free(dst_path);
continue;
}
if (lstat(dst_path, &st) == 0)
{
syslog(LOG_WARNING, "mpl_symlink_ports, dst_path: a symbolic link to the device already exists [%s]", dst_path);
if (unlink(dst_path) == -1)
{
syslog(LOG_ERR, "mpl_symlink_ports, dst_path: link could not be deleted [%s]", dst_path);
free(src_path);
free(dst_path);
continue;
}
}
if (symlink(src_path, dst_path) == 0)
syslog(LOG_NOTICE, "mpl_symlink_ports, dst_path: the symbolic link has been successfully created [%s -> %s]", dst_path, src_path);
else
syslog(LOG_ERR, "mpl_symlink_ports, dst_path: error creating a symbolic link [%s -> %s]", dst_path, src_path);
free(src_path);
free(dst_path);
}
}
void mpl_unlink_ports(MPLmodem *modem)
{
for (guint i = 0; i < modem->n_ports; i++)
{
char *dst_path = mpl_get_dst_path(i, modem->imei);
struct stat st;
if (lstat(dst_path, &st) == -1)
{
syslog(LOG_WARNING, "mpl_unlink_ports, dst_path: a symbolic link to the device not exists [%s]", dst_path);
free(dst_path);
continue;
}
if (unlink(dst_path) == 0)
syslog(LOG_NOTICE, "mpl_unlink_ports, dst_path: link was successfully deleted [%s]", dst_path);
else
syslog(LOG_ERR, "mpl_unlink_ports, dst_path: link could not be deleted [%s]", dst_path);
free(dst_path);
}
}
void mpl_device_added(MMManager *manager, MMObject *obj)
{
MPLmodem *modem = mpl_get_new_modem_info(obj);
if (modem == NULL)
return;
// mpl_print_modem_info(modem, true);
mpl_symlink_ports(modem);
mpl_free_modem(modem);
}
void mpl_device_removed(MMManager *manager, MMObject *obj)
{
MPLmodem *modem = mpl_get_new_modem_info(obj);
if (modem == NULL)
return;
// mpl_print_modem_info(modem, false);
mpl_unlink_ports(modem);
mpl_free_modem(modem);
}

View File

@ -1,90 +0,0 @@
#include "mportlink.h"
#include <sys/file.h>
#include <fcntl.h>
#define LOCKFILE "/run/lock/mportlink.lock"
#define MPL "mportlink"
static Context *ctx;
static GCancellable *cancellable;
static GMainLoop *loop;
static void signals_handler(int signum)
{
if (cancellable)
{
if (!g_cancellable_is_cancelled(cancellable))
{
syslog(LOG_WARNING, "signals_handler: cancelling the operation...");
g_cancellable_cancel(cancellable);
}
}
if (loop && g_main_loop_is_running(loop))
{
syslog(LOG_WARNING, "signals_handler: cancelling the main loop...");
g_main_loop_quit(loop);
}
}
static void get_manager_ready(GObject *source, GAsyncResult *result, gpointer none)
{
ctx->manager = mmcli_get_manager_finish(result);
g_signal_connect(ctx->manager, "object-added", G_CALLBACK(mpl_device_added), NULL);
g_signal_connect(ctx->manager, "object-removed", G_CALLBACK(mpl_device_removed), NULL);
}
int main()
{
openlog(MPL, LOG_PID, LOG_SYSLOG);
int lock_fd = open(LOCKFILE, O_CREAT | O_RDWR, 0666);
int rc = flock(lock_fd, LOCK_EX | LOCK_NB);
if (rc || errno == EWOULDBLOCK)
{
syslog(LOG_ERR, "main: only one instance of %s is allowed", MPL);
exit(EXIT_FAILURE);
}
syslog(LOG_NOTICE, "main: starting the %s daemon", MPL);
mpl_create_dongle_dir();
GError *error = NULL;
GDBusConnection *connection = NULL;
signal(SIGINT, signals_handler);
signal(SIGHUP, signals_handler);
signal(SIGTERM, signals_handler);
connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
if (!connection)
{
syslog(LOG_ERR, "main, connection: couldn't get bus: %s", error->message);
exit(EXIT_FAILURE);
}
cancellable = g_cancellable_new();
loop = g_main_loop_new(NULL, FALSE);
ctx = g_new0(Context, 1);
if (cancellable)
ctx->cancellable = g_object_ref(cancellable);
mmcli_get_manager(connection, cancellable, (GAsyncReadyCallback)get_manager_ready, NULL);
g_main_loop_run(loop);
if (cancellable)
g_object_unref(cancellable);
g_main_loop_unref(loop);
g_object_unref(connection);
flock(lock_fd, LOCK_UN);
syslog(LOG_NOTICE, "main: stopping the %s daemon", MPL);
closelog();
return EXIT_SUCCESS;
}

View File

@ -1,5 +0,0 @@
#include "mm.h"
void mpl_create_dongle_dir();
void mpl_device_added(MMManager *manager, MMObject *obj);
void mpl_device_removed(MMManager *manager, MMObject *obj);