mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-01-01 01:07:41 +00:00
Update denylist config implementation
This commit is contained in:
parent
c0be5383de
commit
706a492218
@ -6,9 +6,9 @@ import android.util.Xml
|
|||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.topjohnwu.magisk.BuildConfig
|
import com.topjohnwu.magisk.BuildConfig
|
||||||
import com.topjohnwu.magisk.core.utils.BiometricHelper
|
|
||||||
import com.topjohnwu.magisk.core.utils.refreshLocale
|
import com.topjohnwu.magisk.core.utils.refreshLocale
|
||||||
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
import com.topjohnwu.magisk.data.preference.PreferenceModel
|
||||||
|
import com.topjohnwu.magisk.data.repository.DBBoolSettingsNoWrite
|
||||||
import com.topjohnwu.magisk.data.repository.DBConfig
|
import com.topjohnwu.magisk.data.repository.DBConfig
|
||||||
import com.topjohnwu.magisk.di.ServiceLocator
|
import com.topjohnwu.magisk.di.ServiceLocator
|
||||||
import com.topjohnwu.magisk.ui.theme.Theme
|
import com.topjohnwu.magisk.ui.theme.Theme
|
||||||
@ -38,6 +38,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
const val SU_MNT_NS = "mnt_ns"
|
const val SU_MNT_NS = "mnt_ns"
|
||||||
const val SU_BIOMETRIC = "su_biometric"
|
const val SU_BIOMETRIC = "su_biometric"
|
||||||
const val ZYGISK = "zygisk"
|
const val ZYGISK = "zygisk"
|
||||||
|
const val DENYLIST = "denylist"
|
||||||
const val SU_MANAGER = "requester"
|
const val SU_MANAGER = "requester"
|
||||||
const val KEYSTORE = "keystore"
|
const val KEYSTORE = "keystore"
|
||||||
|
|
||||||
@ -145,6 +146,7 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY)
|
||||||
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
var suBiometric by dbSettings(Key.SU_BIOMETRIC, false)
|
||||||
var zygisk by dbSettings(Key.ZYGISK, false)
|
var zygisk by dbSettings(Key.ZYGISK, false)
|
||||||
|
var denyList by DBBoolSettingsNoWrite(Key.DENYLIST, false)
|
||||||
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
var suManager by dbStrings(Key.SU_MANAGER, "", true)
|
||||||
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
var keyStoreRaw by dbStrings(Key.KEYSTORE, "", true)
|
||||||
|
|
||||||
@ -169,12 +171,6 @@ object Config : PreferenceModel, DBConfig {
|
|||||||
else if (it.toInt() > Value.CANARY_CHANNEL)
|
else if (it.toInt() > Value.CANARY_CHANNEL)
|
||||||
putString(Key.UPDATE_CHANNEL, Value.CANARY_CHANNEL.toString())
|
putString(Key.UPDATE_CHANNEL, Value.CANARY_CHANNEL.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write database configs
|
|
||||||
putString(Key.ROOT_ACCESS, rootMode.toString())
|
|
||||||
putString(Key.SU_MNT_NS, suMntNamespaceMode.toString())
|
|
||||||
putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString())
|
|
||||||
putBoolean(Key.SU_BIOMETRIC, BiometricHelper.isEnabled)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,9 +64,5 @@ object Info {
|
|||||||
}
|
}
|
||||||
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
val isUnsupported = code > 0 && code < Const.Version.MIN_VERCODE
|
||||||
val isActive = magiskVersionCode >= 0
|
val isActive = magiskVersionCode >= 0
|
||||||
var denyListEnforced = if (Const.Version.isCanary(code))
|
|
||||||
Shell.su("magisk --denylist status").exec().isSuccess
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,12 @@ class DBSettingsValue(
|
|||||||
private val default: Int
|
private val default: Int
|
||||||
) : ReadWriteProperty<DBConfig, Int> {
|
) : ReadWriteProperty<DBConfig, Int> {
|
||||||
|
|
||||||
private var value: Int? = null
|
var value: Int? = null
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun getValue(thisRef: DBConfig, property: KProperty<*>): Int {
|
override fun getValue(thisRef: DBConfig, property: KProperty<*>): Int {
|
||||||
if (value == null)
|
if (value == null)
|
||||||
value = runBlocking {
|
value = runBlocking { thisRef.settingsDB.fetch(name, default) }
|
||||||
thisRef.settingsDB.fetch(name, default)
|
|
||||||
}
|
|
||||||
return value as Int
|
return value as Int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +54,7 @@ class DBSettingsValue(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DBBoolSettings(
|
open class DBBoolSettings(
|
||||||
name: String,
|
name: String,
|
||||||
default: Boolean
|
default: Boolean
|
||||||
) : ReadWriteProperty<DBConfig, Boolean> {
|
) : ReadWriteProperty<DBConfig, Boolean> {
|
||||||
@ -70,6 +68,17 @@ class DBBoolSettings(
|
|||||||
base.setValue(thisRef, property, if (value) 1 else 0)
|
base.setValue(thisRef, property, if (value) 1 else 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DBBoolSettingsNoWrite(
|
||||||
|
name: String,
|
||||||
|
default: Boolean
|
||||||
|
) : DBBoolSettings(name, default) {
|
||||||
|
override fun setValue(thisRef: DBConfig, property: KProperty<*>, value: Boolean) {
|
||||||
|
synchronized(base) {
|
||||||
|
base.value = if (value) 1 else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DBStringsValue(
|
class DBStringsValue(
|
||||||
private val name: String,
|
private val name: String,
|
||||||
private val default: String,
|
private val default: String,
|
||||||
|
@ -251,11 +251,11 @@ object DenyList : BaseSettingsItem.Toggle() {
|
|||||||
if (isEnabled) R.string.settings_denylist_summary.asText()
|
if (isEnabled) R.string.settings_denylist_summary.asText()
|
||||||
else R.string.settings_denylist_error.asText(R.string.zygisk.asText())
|
else R.string.settings_denylist_error.asText(R.string.zygisk.asText())
|
||||||
|
|
||||||
override var value = Info.env.denyListEnforced
|
override var value = Config.denyList
|
||||||
set(value) = setV(value, field, { field = it }) {
|
set(value) = setV(value, field, { field = it }) {
|
||||||
val cmd = if (it) "enable" else "disable"
|
val cmd = if (it) "enable" else "disable"
|
||||||
Shell.su("magisk --denylist $cmd").submit { result ->
|
Shell.su("magisk --denylist $cmd").submit { result ->
|
||||||
if (result.isSuccess) Info.env.denyListEnforced = it
|
if (result.isSuccess) Config.denyList = it
|
||||||
else field = !it
|
else field = !it
|
||||||
}
|
}
|
||||||
DenyListConfig.isEnabled = it
|
DenyListConfig.isEnabled = it
|
||||||
|
@ -304,10 +304,8 @@ void post_fs_data(int client) {
|
|||||||
exec_common_scripts("post-fs-data");
|
exec_common_scripts("post-fs-data");
|
||||||
db_settings dbs;
|
db_settings dbs;
|
||||||
get_db_settings(dbs, ZYGISK_CONFIG);
|
get_db_settings(dbs, ZYGISK_CONFIG);
|
||||||
if (dbs[ZYGISK_CONFIG]) {
|
zygisk_enabled = dbs[ZYGISK_CONFIG];
|
||||||
zygisk_enabled = true;
|
initialize_denylist();
|
||||||
check_enforce_denylist();
|
|
||||||
}
|
|
||||||
handle_modules();
|
handle_modules();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,15 @@
|
|||||||
|
|
||||||
extern bool RECOVERY_MODE;
|
extern bool RECOVERY_MODE;
|
||||||
extern int DAEMON_STATE;
|
extern int DAEMON_STATE;
|
||||||
extern bool zygisk_enabled;
|
|
||||||
|
// Daemon state
|
||||||
|
enum : int {
|
||||||
|
STATE_NONE,
|
||||||
|
STATE_POST_FS_DATA,
|
||||||
|
STATE_POST_FS_DATA_DONE,
|
||||||
|
STATE_LATE_START_DONE,
|
||||||
|
STATE_BOOT_COMPLETE
|
||||||
|
};
|
||||||
|
|
||||||
void unlock_blocks();
|
void unlock_blocks();
|
||||||
void reboot();
|
void reboot();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <utils.hpp>
|
#include <utils.hpp>
|
||||||
#include <magisk.hpp>
|
#include <magisk.hpp>
|
||||||
|
#include <daemon.hpp>
|
||||||
#include <selinux.hpp>
|
#include <selinux.hpp>
|
||||||
#include <resetprop.hpp>
|
#include <resetprop.hpp>
|
||||||
|
|
||||||
|
@ -35,11 +35,10 @@ void denylist_handler(int client, ucred *cred) {
|
|||||||
int res = DAEMON_ERROR;
|
int res = DAEMON_ERROR;
|
||||||
|
|
||||||
switch (req) {
|
switch (req) {
|
||||||
case DISABLE_DENY:
|
|
||||||
case ADD_LIST:
|
case ADD_LIST:
|
||||||
case RM_LIST:
|
case RM_LIST:
|
||||||
case LS_LIST:
|
case LS_LIST:
|
||||||
if (!deny_enforced()) {
|
if (!denylist_enabled) {
|
||||||
write_int(client, DENY_NOT_ENFORCED);
|
write_int(client, DENY_NOT_ENFORCED);
|
||||||
close(client);
|
close(client);
|
||||||
return;
|
return;
|
||||||
@ -48,7 +47,7 @@ void denylist_handler(int client, ucred *cred) {
|
|||||||
|
|
||||||
switch (req) {
|
switch (req) {
|
||||||
case ENFORCE_DENY:
|
case ENFORCE_DENY:
|
||||||
res = enable_hide();
|
res = enable_deny();
|
||||||
break;
|
break;
|
||||||
case DISABLE_DENY:
|
case DISABLE_DENY:
|
||||||
res = disable_deny();
|
res = disable_deny();
|
||||||
@ -63,7 +62,7 @@ void denylist_handler(int client, ucred *cred) {
|
|||||||
ls_list(client);
|
ls_list(client);
|
||||||
return;
|
return;
|
||||||
case DENY_STATUS:
|
case DENY_STATUS:
|
||||||
res = deny_enforced() ? DENY_IS_ENFORCED : DENY_NOT_ENFORCED;
|
res = (zygisk_enabled && denylist_enabled) ? DENY_IS_ENFORCED : DENY_NOT_ENFORCED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,26 +4,28 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include <daemon.hpp>
|
#include <daemon.hpp>
|
||||||
|
|
||||||
#define ISOLATED_MAGIC "isolated"
|
#define ISOLATED_MAGIC "isolated"
|
||||||
|
|
||||||
// CLI entries
|
// CLI entries
|
||||||
int enable_hide();
|
int enable_deny();
|
||||||
int disable_deny();
|
int disable_deny();
|
||||||
int add_list(int client);
|
int add_list(int client);
|
||||||
int rm_list(int client);
|
int rm_list(int client);
|
||||||
void ls_list(int client);
|
void ls_list(int client);
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
bool deny_enforced();
|
|
||||||
bool is_deny_target(int uid, std::string_view process);
|
bool is_deny_target(int uid, std::string_view process);
|
||||||
|
|
||||||
// Revert
|
// Revert
|
||||||
void revert_daemon(int pid, int client);
|
void revert_daemon(int pid, int client);
|
||||||
void revert_unmount(int pid = -1);
|
void revert_unmount(int pid = -1);
|
||||||
|
|
||||||
|
extern std::atomic<bool> denylist_enabled;
|
||||||
|
|
||||||
enum : int {
|
enum : int {
|
||||||
ENFORCE_DENY,
|
ENFORCE_DENY,
|
||||||
DISABLE_DENY,
|
DISABLE_DENY,
|
||||||
|
@ -19,7 +19,7 @@ static map<int, vector<string_view>> *uid_proc_map; /* uid -> list of process *
|
|||||||
// Locks the variables above
|
// Locks the variables above
|
||||||
static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t data_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static atomic<bool> enforcement_status = false;
|
atomic<bool> denylist_enabled = false;
|
||||||
|
|
||||||
static void rebuild_uid_map() {
|
static void rebuild_uid_map() {
|
||||||
uid_proc_map->clear();
|
uid_proc_map->clear();
|
||||||
@ -147,7 +147,6 @@ static int add_list(const char *pkg, const char *proc) {
|
|||||||
return DENYLIST_INVALID_PKG;
|
return DENYLIST_INVALID_PKG;
|
||||||
|
|
||||||
{
|
{
|
||||||
// Critical region
|
|
||||||
mutex_guard lock(data_lock);
|
mutex_guard lock(data_lock);
|
||||||
for (const auto &hide : *deny_set)
|
for (const auto &hide : *deny_set)
|
||||||
if (hide.first == pkg && hide.second == proc)
|
if (hide.first == pkg && hide.second == proc)
|
||||||
@ -172,9 +171,8 @@ int add_list(int client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int rm_list(const char *pkg, const char *proc) {
|
static int rm_list(const char *pkg, const char *proc) {
|
||||||
bool remove = false;
|
|
||||||
{
|
{
|
||||||
// Critical region
|
bool remove = false;
|
||||||
mutex_guard lock(data_lock);
|
mutex_guard lock(data_lock);
|
||||||
for (auto it = deny_set->begin(); it != deny_set->end();) {
|
for (auto it = deny_set->begin(); it != deny_set->end();) {
|
||||||
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
|
if (it->first == pkg && (proc[0] == '\0' || it->second == proc)) {
|
||||||
@ -248,63 +246,68 @@ void ls_list(int client) {
|
|||||||
close(client);
|
close(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_hide_config() {
|
static void update_deny_config() {
|
||||||
char sql[64];
|
char sql[64];
|
||||||
sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)",
|
sprintf(sql, "REPLACE INTO settings (key,value) VALUES('%s',%d)",
|
||||||
DB_SETTING_KEYS[DENYLIST_CONFIG], enforcement_status.load());
|
DB_SETTING_KEYS[DENYLIST_CONFIG], denylist_enabled.load());
|
||||||
char *err = db_exec(sql);
|
char *err = db_exec(sql);
|
||||||
db_err(err);
|
db_err(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
int enable_hide() {
|
int enable_deny() {
|
||||||
if (enforcement_status)
|
if (denylist_enabled) {
|
||||||
return DENY_IS_ENFORCED;
|
return DAEMON_SUCCESS;
|
||||||
|
} else {
|
||||||
|
mutex_guard lock(data_lock);
|
||||||
|
|
||||||
if (access("/proc/self/ns/mnt", F_OK) != 0)
|
if (access("/proc/self/ns/mnt", F_OK) != 0) {
|
||||||
return DENY_NO_NS;
|
LOGW("The kernel does not support mount namespace\n");
|
||||||
|
return DENY_NO_NS;
|
||||||
|
}
|
||||||
|
|
||||||
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
|
if (procfp == nullptr && (procfp = opendir("/proc")) == nullptr)
|
||||||
return DAEMON_ERROR;
|
return DAEMON_ERROR;
|
||||||
|
|
||||||
LOGI("* Enforce DenyList\n");
|
LOGI("* Enable DenyList\n");
|
||||||
|
|
||||||
mutex_guard lock(data_lock);
|
default_new(deny_set);
|
||||||
default_new(deny_set);
|
if (!init_list()) {
|
||||||
default_new(uid_proc_map);
|
delete deny_set;
|
||||||
|
deny_set = nullptr;
|
||||||
|
return DAEMON_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the hide list
|
denylist_enabled = true;
|
||||||
if (!init_list())
|
|
||||||
return DAEMON_ERROR;
|
|
||||||
|
|
||||||
enforcement_status = true;
|
default_new(uid_proc_map);
|
||||||
update_hide_config();
|
rebuild_uid_map();
|
||||||
|
}
|
||||||
|
|
||||||
rebuild_uid_map();
|
update_deny_config();
|
||||||
return DAEMON_SUCCESS;
|
return DAEMON_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int disable_deny() {
|
int disable_deny() {
|
||||||
mutex_guard lock(data_lock);
|
if (denylist_enabled) {
|
||||||
|
denylist_enabled = false;
|
||||||
if (enforcement_status) {
|
|
||||||
LOGI("* Disable DenyList\n");
|
LOGI("* Disable DenyList\n");
|
||||||
|
|
||||||
|
mutex_guard lock(data_lock);
|
||||||
delete uid_proc_map;
|
delete uid_proc_map;
|
||||||
delete deny_set;
|
delete deny_set;
|
||||||
uid_proc_map = nullptr;
|
uid_proc_map = nullptr;
|
||||||
deny_set = nullptr;
|
deny_set = nullptr;
|
||||||
}
|
}
|
||||||
|
update_deny_config();
|
||||||
enforcement_status = false;
|
|
||||||
update_hide_config();
|
|
||||||
return DAEMON_SUCCESS;
|
return DAEMON_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_enforce_denylist() {
|
void initialize_denylist() {
|
||||||
if (!enforcement_status) {
|
if (!denylist_enabled) {
|
||||||
db_settings dbs;
|
db_settings dbs;
|
||||||
get_db_settings(dbs, DENYLIST_CONFIG);
|
get_db_settings(dbs, DENYLIST_CONFIG);
|
||||||
if (dbs[DENYLIST_CONFIG])
|
if (dbs[DENYLIST_CONFIG])
|
||||||
enable_hide();
|
enable_deny();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +336,3 @@ bool is_deny_target(int uid, string_view process) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool deny_enforced() {
|
|
||||||
return enforcement_status;
|
|
||||||
}
|
|
||||||
|
@ -39,14 +39,7 @@ enum : int {
|
|||||||
DAEMON_LAST
|
DAEMON_LAST
|
||||||
};
|
};
|
||||||
|
|
||||||
// Daemon state
|
extern bool zygisk_enabled;
|
||||||
enum : int {
|
|
||||||
STATE_NONE,
|
|
||||||
STATE_POST_FS_DATA,
|
|
||||||
STATE_POST_FS_DATA_DONE,
|
|
||||||
STATE_LATE_START_DONE,
|
|
||||||
STATE_BOOT_COMPLETE
|
|
||||||
};
|
|
||||||
|
|
||||||
int connect_daemon(bool create = false);
|
int connect_daemon(bool create = false);
|
||||||
|
|
||||||
@ -63,6 +56,6 @@ void su_daemon_handler(int client, ucred *credential);
|
|||||||
void zygisk_handler(int client, ucred *cred);
|
void zygisk_handler(int client, ucred *cred);
|
||||||
|
|
||||||
// Denylist
|
// Denylist
|
||||||
void check_enforce_denylist();
|
void initialize_denylist();
|
||||||
int disable_deny();
|
int disable_deny();
|
||||||
int denylist_cli(int argc, char **argv);
|
int denylist_cli(int argc, char **argv);
|
||||||
|
@ -26,7 +26,7 @@ int fd_pathat(int dirfd, const char *name, char *path, size_t size) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mkdirs(string_view path, mode_t mode) {
|
int mkdirs(string path, mode_t mode) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
for (char *p = path.data() + 1; *p; ++p) {
|
for (char *p = path.data() + 1; *p; ++p) {
|
||||||
if (*p == '/') {
|
if (*p == '/') {
|
||||||
|
@ -32,7 +32,7 @@ struct raw_file {
|
|||||||
|
|
||||||
ssize_t fd_path(int fd, char *path, size_t size);
|
ssize_t fd_path(int fd, char *path, size_t size);
|
||||||
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
int fd_pathat(int dirfd, const char *name, char *path, size_t size);
|
||||||
int mkdirs(std::string_view path, mode_t mode);
|
int mkdirs(std::string path, mode_t mode);
|
||||||
void rm_rf(const char *path);
|
void rm_rf(const char *path);
|
||||||
void mv_path(const char *src, const char *dest);
|
void mv_path(const char *src, const char *dest);
|
||||||
void mv_dir(int src, int dest);
|
void mv_dir(int src, int dest);
|
||||||
|
@ -255,7 +255,7 @@ static void setup_files(int client, ucred *cred) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void check_denylist(int client) {
|
static void check_denylist(int client) {
|
||||||
if (!deny_enforced()) {
|
if (!denylist_enabled) {
|
||||||
write_int(client, DENY_NOT_ENFORCED);
|
write_int(client, DENY_NOT_ENFORCED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -266,7 +266,7 @@ static void check_denylist(int client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void do_unmount(int client, ucred *cred) {
|
static void do_unmount(int client, ucred *cred) {
|
||||||
if (deny_enforced()) {
|
if (denylist_enabled) {
|
||||||
LOGD("zygisk: cleanup mount namespace for pid=[%d]\n", cred->pid);
|
LOGD("zygisk: cleanup mount namespace for pid=[%d]\n", cred->pid);
|
||||||
revert_daemon(cred->pid, client);
|
revert_daemon(cred->pid, client);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user