Add mirror 'dinit-userservd' at '0745b20'
This commit is contained in:
parent
8562840e64
commit
3e67522af5
10 changed files with 1842 additions and 0 deletions
|
@ -1,6 +1,11 @@
|
|||
{
|
||||
"config_version": 1,
|
||||
"mirrors": {
|
||||
"dinit-userservd": {
|
||||
"url": "https://github.com/XynonWasTaken/dinit-userservd",
|
||||
"branch": "master",
|
||||
"revision": "0745b208d6a742e9b6e8e168a1dfea707f2c11be"
|
||||
},
|
||||
"linux-xanmod-edge": {
|
||||
"url": "https://aur.archlinux.org/linux-xanmod-edge.git",
|
||||
"branch": "master",
|
||||
|
|
22
dinit-userservd/COPYING.md
Normal file
22
dinit-userservd/COPYING.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
Copyright 2021 Daniel "q66" Kolesa
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
33
dinit-userservd/PKGBUILD
Normal file
33
dinit-userservd/PKGBUILD
Normal file
|
@ -0,0 +1,33 @@
|
|||
pkgname=dinit-userservd
|
||||
pkgver=0.1.1
|
||||
pkgrel=1
|
||||
epoch=
|
||||
pkgdesc="user dinit instance spawner + manager daemon."
|
||||
arch=('any')
|
||||
url="https://github.com/chimera-linux/dinit-userservd/"
|
||||
license=('BSD')
|
||||
groups=('dinit-system')
|
||||
depends=('dinit' 'elogind')
|
||||
makedepends=('meson')
|
||||
optdepends=()
|
||||
provides=('dinit-userservd')
|
||||
conflicts=('dinit-userservd-git')
|
||||
install='dinit-userservd.install'
|
||||
changelog=
|
||||
|
||||
prepare() {
|
||||
cd $startdir
|
||||
rm -rf pkg
|
||||
mkdir -p build
|
||||
}
|
||||
|
||||
build() {
|
||||
cd $startdir
|
||||
meson --prefix=/usr --buildtype=plain ./ ./build
|
||||
meson compile -C build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd $startdir
|
||||
meson install -C build --destdir "$pkgdir"
|
||||
}
|
97
dinit-userservd/README.md
Normal file
97
dinit-userservd/README.md
Normal file
|
@ -0,0 +1,97 @@
|
|||
# Artix Specific Info
|
||||
|
||||
PKGBUILDs for dinit-userservd located at: https://github.com/XynonWasTaken/dinit-userservd-PKGBUILD
|
||||
|
||||
Artix services for dinit-userservd located at: https://github.com/XynonWasTaken/dinit-userservd-services
|
||||
|
||||
# dinit-userservd
|
||||
|
||||
This is a daemon and a PAM module to handle user services management with the
|
||||
`dinit` init system and service manager (https://github.com/davmac314/dinit).
|
||||
|
||||
It was created for the needs of the Chimera Linux project. It is not expected
|
||||
to work properly anywhere else by default (those use cases are unsupported),
|
||||
and issues or feature requests specific to other environments will not be
|
||||
addressed. Patches may be accepted, provided they are not disruptive or
|
||||
introduce excessive complexity.
|
||||
|
||||
## How it works
|
||||
|
||||
The project consists of a daemon and a PAM module. The PAM module is enabled
|
||||
for example by adding this in your login path:
|
||||
|
||||
```
|
||||
session optional pam_dinit_userservd.so
|
||||
```
|
||||
|
||||
The daemon must simply be running in some way. If it is not running, you will
|
||||
still be able to log in with the above setup, but it will not do anything.
|
||||
|
||||
A recommended way to manage the daemon is using a `dinit` service that is
|
||||
provided with the project.
|
||||
|
||||
The daemon opens a control socket. The PAM module will make connections to
|
||||
it upon session start (and close it upon session end). When the daemon
|
||||
receives a connection, it will negotiate a session with the PAM module
|
||||
and upon first login of each user, spawn a user `dinit` instance.
|
||||
|
||||
This instance is supervised, if it fails in any way it gets automatically
|
||||
restarted. It runs outside of the login itself, as only one instance must
|
||||
exist per user (who can have multiple logins) and it only exists once the
|
||||
last login has logged out. This means that environment variables of the
|
||||
login do not exist within the user instance by default, and they must be
|
||||
exported into it through other means.
|
||||
|
||||
It will register the following service directories:
|
||||
|
||||
* `~/.config/dinit.d`
|
||||
* `/etc/dinit.d/user`
|
||||
* `/usr/local/lib/dinit.d/user`
|
||||
* `/usr/lib/dinit.d/user`
|
||||
|
||||
You do not need to provide a `boot` service (in fact, you should not).
|
||||
By default, the following path is used for autostarted user services:
|
||||
|
||||
* `~/.config/dinit.d/boot.d`
|
||||
|
||||
Simply drop symlinks to whatever services you want in there and they will
|
||||
get started with your login.
|
||||
|
||||
The login proceeds once the `dinit` instance has signaled readiness (which
|
||||
is once it has started its autostart services). It does so via an internal
|
||||
notification mechanism.
|
||||
|
||||
### XDG_RUNTIME_DIR handling
|
||||
|
||||
**NOTE:** This is problematic for now, so it's disabled at the moment.
|
||||
|
||||
Usually, `XDG_RUNTIME_DIR` is managed by another daemon, typically `elogind`
|
||||
for Chimera. However, some people may not be running `elogind` or a similar
|
||||
solution. The PAM module automatically detects this and makes the daemon
|
||||
manage the runtime directory for you.
|
||||
|
||||
It takes care of both creation and cleanup automatically as sessions are
|
||||
logged in and as they go away.
|
||||
|
||||
To prevent it from managing rundir, you simply have to have something else
|
||||
manage it before; that means specifying that earlier in the PAM config file.
|
||||
Or, if you want to force that off, you can pass the `norundir` extra PAM
|
||||
argument.
|
||||
|
||||
### Dbus handling
|
||||
|
||||
The daemon also supports handling of D-Bus session bus. If the socket
|
||||
`/run/user/UID/bus` exists by the time readiness has been signaled, the
|
||||
variable `DBUS_SESSION_BUS_ADDRESS` will automatically be exported into
|
||||
the login environment.
|
||||
|
||||
That way it is possible to manage the session bus as a user service without
|
||||
having to spawn it on-demand.
|
||||
|
||||
User services making use of the bus need to ensure that the variable is
|
||||
exported in their launch environment in some way, as the service manager
|
||||
runs outside of the user's login session.
|
||||
|
||||
## TODO
|
||||
|
||||
* Do not hardcode things to make it easier to use for other projects.
|
7
dinit-userservd/dinit-userservd
Normal file
7
dinit-userservd/dinit-userservd
Normal file
|
@ -0,0 +1,7 @@
|
|||
# dinit-userservd service
|
||||
|
||||
type = process
|
||||
command = /usr/bin/dinit-userservd
|
||||
depends-on = elogind
|
||||
smooth-recovery = true
|
||||
logfile = /var/log/dinit-userservd.log
|
1161
dinit-userservd/dinit-userservd.cc
Normal file
1161
dinit-userservd/dinit-userservd.cc
Normal file
File diff suppressed because it is too large
Load diff
15
dinit-userservd/dinit-userservd.install
Normal file
15
dinit-userservd/dinit-userservd.install
Normal file
|
@ -0,0 +1,15 @@
|
|||
post_upgrade() {
|
||||
echo """
|
||||
POST INSTALL INSTRUCTIONS
|
||||
---------------------------------
|
||||
you must add: session optional pam_dinit_userservd.so
|
||||
to your /etc/pam.d/login or this package WILL NOT WORK
|
||||
---------------------------------
|
||||
please install user service files to one of these locations (symlink to boot.d in config dir to enable):
|
||||
~/.config/dinit.d/
|
||||
/etc/dinit/user/
|
||||
/usr/lib/dinit.d/user/
|
||||
/usr/local/lib/dinit.d/user/
|
||||
---------------------------------
|
||||
"""
|
||||
}
|
37
dinit-userservd/meson.build
Normal file
37
dinit-userservd/meson.build
Normal file
|
@ -0,0 +1,37 @@
|
|||
project(
|
||||
'dinit-userservd',
|
||||
['cpp'],
|
||||
version: '0.1.0',
|
||||
default_options: [
|
||||
'cpp_std=c++17', 'warning_level=3', 'buildtype=debugoptimized',
|
||||
'cpp_eh=none', 'cpp_rtti=false',
|
||||
],
|
||||
license: 'BSD-2-Clause'
|
||||
)
|
||||
|
||||
cpp = meson.get_compiler('cpp')
|
||||
|
||||
pam_dep = dependency('pam', required: true)
|
||||
rt_dep = cpp.find_library('rt', required: false)
|
||||
|
||||
daemon = executable(
|
||||
'dinit-userservd', 'dinit-userservd.cc',
|
||||
install: true,
|
||||
dependencies: [rt_dep],
|
||||
gnu_symbol_visibility: 'hidden'
|
||||
)
|
||||
|
||||
pam_mod = shared_module(
|
||||
'pam_dinit_userservd', 'pam_dinit_userservd.cc',
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('libdir'), 'security'),
|
||||
name_prefix: '',
|
||||
dependencies: [pam_dep],
|
||||
gnu_symbol_visibility: 'hidden'
|
||||
)
|
||||
|
||||
install_data(
|
||||
'dinit-userservd',
|
||||
install_dir: join_paths(get_option('sysconfdir'), 'dinit.d'),
|
||||
install_mode: 'rw-r--r--'
|
||||
)
|
365
dinit-userservd/pam_dinit_userservd.cc
Normal file
365
dinit-userservd/pam_dinit_userservd.cc
Normal file
|
@ -0,0 +1,365 @@
|
|||
/* pam_dinit_userservd: the client part of dinit-userservd
|
||||
*
|
||||
* it connects to its socket and requests logins/logouts,
|
||||
* communicating over a rudimentary protocol
|
||||
*
|
||||
* the PAM session opens a persistent connection, which also
|
||||
* takes care of tracking when a session needs ending on the
|
||||
* daemon side (once all connections are gone)
|
||||
*
|
||||
* Copyright 2021 Daniel "q66" Kolesa <q66@chimera-linux.org>
|
||||
* License: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <security/pam_modules.h>
|
||||
#include <security/pam_misc.h>
|
||||
|
||||
#include "protocol.hh"
|
||||
|
||||
#define PAMAPI __attribute__((visibility ("default")))
|
||||
|
||||
static void free_sock(pam_handle_t *, void *data, int) {
|
||||
int sock = *static_cast<int *>(data);
|
||||
if (sock != -1) {
|
||||
close(sock);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool open_session(
|
||||
pam_handle_t *pamh, unsigned int &uid, int argc, char const **argv,
|
||||
unsigned int &orlen, char *orbuf, bool &set_rundir
|
||||
) {
|
||||
int *sock = static_cast<int *>(std::malloc(sizeof(int)));
|
||||
if (!sock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: this is problematic with gdm somehow, figure out why */
|
||||
bool do_rundir = true;
|
||||
|
||||
/* overrides */
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (!std::strcmp(argv[i], "norundir")) {
|
||||
do_rundir = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool do_rundir = false;
|
||||
#endif
|
||||
|
||||
/* blocking socket and a simple protocol */
|
||||
*sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
|
||||
if (*sock == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* associate the socket with the session */
|
||||
if (pam_set_data(
|
||||
pamh, "pam_dinit_session", sock, free_sock
|
||||
) != PAM_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sockaddr_un saddr;
|
||||
std::memset(&saddr, 0, sizeof(saddr));
|
||||
|
||||
saddr.sun_family = AF_UNIX;
|
||||
std::memcpy(saddr.sun_path, DAEMON_SOCK, sizeof(DAEMON_SOCK));
|
||||
|
||||
char const *puser;
|
||||
char const *hdir;
|
||||
char const *rdir;
|
||||
passwd *pwd;
|
||||
int ret, hlen, rlen;
|
||||
|
||||
auto send_msg = [sock](unsigned int msg) {
|
||||
if (write(*sock, &msg, sizeof(msg)) < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (pam_get_user(pamh, &puser, nullptr) != PAM_SUCCESS) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
pwd = getpwnam(puser);
|
||||
if (!pwd) {
|
||||
goto err;
|
||||
}
|
||||
uid = pwd->pw_uid;
|
||||
|
||||
hdir = pam_getenv(pamh, "HOME");
|
||||
if (!hdir || !hdir[0]) {
|
||||
hdir = pwd->pw_dir;
|
||||
}
|
||||
if (!hdir || !hdir[0]) {
|
||||
goto err;
|
||||
}
|
||||
hlen = strlen(hdir);
|
||||
if (hlen > DIRLEN_MAX) {
|
||||
goto err;
|
||||
}
|
||||
/* this is verified serverside too but bail out early if needed */
|
||||
if (struct stat s; stat(hdir, &s) || !S_ISDIR(s.st_mode)) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* the other runtime dir manager is expected to ensure that the
|
||||
* rundir actually exists by this point (logind does ensure it)
|
||||
*/
|
||||
rdir = pam_getenv(pamh, "XDG_RUNTIME_DIR");
|
||||
if (!rdir) {
|
||||
rdir = "";
|
||||
}
|
||||
rlen = strlen(rdir);
|
||||
if (rlen > DIRLEN_MAX) {
|
||||
goto err;
|
||||
} else if (rlen == 0) {
|
||||
set_rundir = do_rundir;
|
||||
}
|
||||
|
||||
if (connect(
|
||||
*sock, reinterpret_cast<sockaddr const *>(&saddr), sizeof(saddr)
|
||||
) < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!send_msg(MSG_START)) {
|
||||
goto err;
|
||||
}
|
||||
/* main message loop */
|
||||
{
|
||||
unsigned int msg;
|
||||
unsigned int state = 0;
|
||||
bool sent_uid = false;
|
||||
bool sent_gid = false;
|
||||
bool sent_hlen = false;
|
||||
bool sent_rlen = false;
|
||||
bool got_rlen = false;
|
||||
char *rbuf = orbuf;
|
||||
|
||||
auto send_strpkt = [&send_msg](char const *&sdir, int &slen) {
|
||||
unsigned int pkt = 0;
|
||||
auto psize = MSG_SBYTES(slen);
|
||||
std::memcpy(&pkt, sdir, psize);
|
||||
pkt <<= MSG_TYPE_BITS;
|
||||
pkt |= MSG_DATA;
|
||||
if (!send_msg(pkt)) {
|
||||
return false;
|
||||
}
|
||||
sdir += psize;
|
||||
slen -= psize;
|
||||
return true;
|
||||
};
|
||||
|
||||
for (;;) {
|
||||
ret = read(*sock, &msg, sizeof(msg));
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
switch (state) {
|
||||
case 0:
|
||||
/* session not established yet */
|
||||
if (msg != MSG_OK) {
|
||||
goto err;
|
||||
}
|
||||
/* send uid */
|
||||
if (!sent_uid) {
|
||||
if (!send_msg(MSG_ENCODE(pwd->pw_uid))) {
|
||||
goto err;
|
||||
}
|
||||
sent_uid = true;
|
||||
break;
|
||||
}
|
||||
/* send gid */
|
||||
if (!sent_gid) {
|
||||
if (!send_msg(MSG_ENCODE(pwd->pw_gid))) {
|
||||
goto err;
|
||||
}
|
||||
sent_gid = true;
|
||||
break;
|
||||
}
|
||||
/* send homedir len */
|
||||
if (!sent_hlen) {
|
||||
if (!send_msg(MSG_ENCODE(hlen))) {
|
||||
goto err;
|
||||
}
|
||||
sent_hlen = true;
|
||||
break;
|
||||
}
|
||||
/* send a piece of homedir */
|
||||
if (hlen) {
|
||||
if (!send_strpkt(hdir, hlen)) {
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* send rundir len */
|
||||
if (!sent_rlen) {
|
||||
auto srlen = rlen;
|
||||
if (!srlen && !do_rundir) {
|
||||
srlen = DIRLEN_MAX + 1;
|
||||
}
|
||||
if (!send_msg(MSG_ENCODE(srlen))) {
|
||||
goto err;
|
||||
}
|
||||
sent_rlen = true;
|
||||
break;
|
||||
}
|
||||
/* send a piece of rundir */
|
||||
if (rlen) {
|
||||
if (!send_strpkt(rdir, rlen)) {
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* send clientside OK */
|
||||
state = msg;
|
||||
if (!send_msg(MSG_OK)) {
|
||||
goto err;
|
||||
}
|
||||
break;
|
||||
case MSG_OK:
|
||||
/* if started, get the rundir back; else block */
|
||||
if ((msg == MSG_OK_DONE) || (msg == MSG_OK_WAIT)) {
|
||||
state = msg;
|
||||
if ((msg == MSG_OK_DONE) && !send_msg(MSG_REQ_RLEN)) {
|
||||
goto err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* bad message */
|
||||
goto err;
|
||||
case MSG_OK_WAIT:
|
||||
/* if we previously waited and now got another message,
|
||||
* it means either an error or that the system is now
|
||||
* fully ready
|
||||
*/
|
||||
if (msg == MSG_OK_DONE) {
|
||||
state = msg;
|
||||
if (!send_msg(MSG_REQ_RLEN)) {
|
||||
goto err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* bad message */
|
||||
goto err;
|
||||
case MSG_OK_DONE: {
|
||||
if ((msg & MSG_TYPE_MASK) != MSG_DATA) {
|
||||
goto err;
|
||||
}
|
||||
/* after MSG_OK_DONE, we should receive the runtime dir
|
||||
* length first; if zero, it means we are completely done
|
||||
*/
|
||||
msg >>= MSG_TYPE_BITS;
|
||||
if (!got_rlen) {
|
||||
if (msg == 0) {
|
||||
orlen = 0;
|
||||
return true;
|
||||
} else if (msg > DIRLEN_MAX) {
|
||||
goto err;
|
||||
}
|
||||
got_rlen = true;
|
||||
rlen = int(msg);
|
||||
orlen = msg;
|
||||
if (!send_msg(MSG_ENCODE_AUX(rlen, MSG_REQ_RDATA))) {
|
||||
goto err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* we are receiving the string... */
|
||||
int pkts = MSG_SBYTES(rlen);
|
||||
std::memcpy(rbuf, &msg, pkts);
|
||||
rbuf += pkts;
|
||||
rlen -= pkts;
|
||||
if (rlen == 0) {
|
||||
/* we have received the whole thing, terminate */
|
||||
*rbuf = '\0';
|
||||
return true;
|
||||
}
|
||||
if (!send_msg(MSG_ENCODE_AUX(rlen, MSG_REQ_RDATA))) {
|
||||
goto err;
|
||||
}
|
||||
/* keep receiving pieces */
|
||||
continue;
|
||||
}
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
close(*sock);
|
||||
*sock = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" PAMAPI int pam_sm_open_session(
|
||||
pam_handle_t *pamh, int, int argc, char const **argv
|
||||
) {
|
||||
unsigned int uid, rlen = 0;
|
||||
bool set_rundir = false;
|
||||
/* potential rundir we are managing */
|
||||
char rdir[DIRLEN_MAX + 1];
|
||||
if (!open_session(pamh, uid, argc, argv, rlen, rdir, set_rundir)) {
|
||||
return PAM_SESSION_ERR;
|
||||
}
|
||||
if (rlen) {
|
||||
char const dpfx[] = "DBUS_SESSION_BUS_ADDRESS=unix:path=";
|
||||
char buf[sizeof(rdir) + sizeof(dpfx) + 4];
|
||||
|
||||
/* try exporting a dbus session bus variable */
|
||||
std::snprintf(buf, sizeof(buf), "%s%s/bus", dpfx, rdir);
|
||||
|
||||
struct stat sbuf;
|
||||
if (!lstat(strchr(buf, '/'), &sbuf) && S_ISSOCK(sbuf.st_mode)) {
|
||||
if (pam_putenv(pamh, buf) != PAM_SUCCESS) {
|
||||
return PAM_SESSION_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
if (!set_rundir) {
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
/* set rundir too if needed */
|
||||
if (pam_misc_setenv(pamh, "XDG_RUNTIME_DIR", rdir, 1) != PAM_SUCCESS) {
|
||||
return PAM_SESSION_ERR;
|
||||
}
|
||||
}
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" PAMAPI int pam_sm_close_session(
|
||||
pam_handle_t *pamh, int, int, char const **
|
||||
) {
|
||||
void const *data;
|
||||
/* there is nothing we can do here */
|
||||
if (pam_get_data(pamh, "pam_dinit_session", &data) != PAM_SUCCESS) {
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
int sock = *static_cast<int const *>(data);
|
||||
if (sock < 0) {
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
/* close the session */
|
||||
close(sock);
|
||||
return PAM_SUCCESS;
|
||||
}
|
100
dinit-userservd/protocol.hh
Normal file
100
dinit-userservd/protocol.hh
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* defines the simple protocol between the daemon and the PAM module
|
||||
*
|
||||
* Copyright 2021 Daniel "q66" Kolesa <q66@chimera-linux.org>
|
||||
* License: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef DINIT_USERSERVD_PROTOCOL_HH
|
||||
#define DINIT_USERSERVD_PROTOCOL_HH
|
||||
|
||||
#include <sys/un.h>
|
||||
|
||||
#define RUNDIR_PATH "/run/user/%u"
|
||||
#define SOCK_PATH "/run/dinit-userservd"
|
||||
#define DAEMON_SOCK SOCK_PATH"/control.sock"
|
||||
#define USER_PATH SOCK_PATH"/%u"
|
||||
#define USER_FIFO USER_PATH"/dinit.fifo"
|
||||
#define USER_DIR USER_PATH"/dinit.XXXXXX"
|
||||
|
||||
/* sanity check */
|
||||
static_assert(
|
||||
sizeof(DAEMON_SOCK) > sizeof(decltype(sockaddr_un{}.sun_family))
|
||||
);
|
||||
|
||||
/* maximum length of a directory path we can receive */
|
||||
#define DIRLEN_MAX 1024
|
||||
|
||||
/* protocol messages
|
||||
*
|
||||
* this is a simple protocol consisting of uint-sized messages; each
|
||||
* message carries the type (4 bits) and optionally auxiliary data
|
||||
* (only some messages; MSG_DATA and MSG_REQ_RDATA)
|
||||
*
|
||||
* dinit-userservd is the server; the pam module is the client
|
||||
*
|
||||
* the client connects to DAEMON_SOCK (seqpacket sockets are used)
|
||||
*
|
||||
* from there, the following sequence happens:
|
||||
*
|
||||
* CLIENT: sends MSG_START and enters a message loop (state machine)
|
||||
* SERVER: receives it and adds the session into pending connections,
|
||||
* then responds MSG_OK
|
||||
* CLIENT: consumes MSG_OK, sends MSG_DATA with user id attached
|
||||
* SERVER: responds MSG_OK
|
||||
* CLIENT: consumes MSG_OK, sends MSG_DATA with group id attached
|
||||
* SERVER: responds MSG_OK
|
||||
* CLIENT: consumes MSG_OK, sends MSG_DATA with homedir length attached
|
||||
* SERVER: validates, allocates a data buffer and responds MSG_OK
|
||||
* loop:
|
||||
* CLIENT: consumes MSG_OK, if there is any of homedir left unsent,
|
||||
* it sends it; otherwise loop ends
|
||||
* SERVER: adds to buffer, responds MSG_OK
|
||||
* CLIENT: consumes MSG_OK, sends MSG_DATA with rundir length attached;
|
||||
* if no rundir is set clientside, sends 0 instead and the server
|
||||
* will make its own; if rundir handling is intentionally skipped,
|
||||
* DIRLEN_MAX+1 is sent instead and the server will disregard it
|
||||
* loop: same as above, but for rundir (nothing is sent for 0 length);
|
||||
* at the end, server acknowledges the session and replies MSG_OK
|
||||
* CLIENT: sends MSG_OK to confirm everything is ready on its side
|
||||
* SERVER: if service manager for the user is already running, responds
|
||||
* with MSG_OK_DONE; else initiates startup and responds with
|
||||
* MSG_OK_WAIT
|
||||
* CLIENT: if MSG_OK_WAIT was received, waits for a message
|
||||
* SERVER: once service manager starts, MSG_OK_DONE is sent
|
||||
* CLIENT: sends MSG_REQ_RLEN
|
||||
* SERVER: responds with MSG_DATA with rundir length (0 if not known)
|
||||
* loop:
|
||||
* CLIENT: sends MSG_REQ_RDATA with number of remaining bytes of rundir
|
||||
* that are yet to be received
|
||||
* SERVER: responds with a MSG_DATA packet until none is left
|
||||
* CLIENT: finishes startup, exports XDG_RUNTIME_DIR if needed as well
|
||||
* as DBUS_SESSION_BUS_ADDRESS, and everything is done
|
||||
*/
|
||||
|
||||
/* this is a regular unsigned int */
|
||||
enum {
|
||||
/* sent by the server as an acknowledgement of a message, and by
|
||||
* the client once it has sent all the session info
|
||||
*/
|
||||
MSG_OK = 0x1,
|
||||
MSG_OK_WAIT, /* login, wait */
|
||||
MSG_OK_DONE, /* ready, proceed */
|
||||
MSG_REQ_RLEN, /* rundir length request */
|
||||
MSG_REQ_RDATA, /* rundir string request + how much is left */
|
||||
MSG_DATA,
|
||||
MSG_START,
|
||||
/* sent by server on errors */
|
||||
MSG_ERR,
|
||||
|
||||
MSG_TYPE_BITS = 4,
|
||||
MSG_TYPE_MASK = 0xF,
|
||||
MSG_DATA_BYTES = sizeof(unsigned int) - 1
|
||||
};
|
||||
|
||||
#define MSG_ENCODE_AUX(v, tp) \
|
||||
(tp | (static_cast<unsigned int>(v) << MSG_TYPE_BITS))
|
||||
|
||||
#define MSG_ENCODE(v) MSG_ENCODE_AUX(v, MSG_DATA)
|
||||
#define MSG_SBYTES(len) std::min(int(MSG_DATA_BYTES), int(len))
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue