Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for config file #75

Merged
merged 8 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .builds/alpine.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ packages:
- pipewire-dev
- wayland-dev
- wayland-protocols
- iniparser-dev
sources:
- https://github.com/emersion/xdg-desktop-portal-wlr
tasks:
Expand Down
1 change: 1 addition & 0 deletions .builds/archlinux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ packages:
- wayland
- wayland-protocols
- pipewire
- iniparser
sources:
- https://github.com/emersion/xdg-desktop-portal-wlr
tasks:
Expand Down
1 change: 1 addition & 0 deletions .builds/freebsd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ packages:
- pkgconf
- wayland
- wayland-protocols
- iniparser
sources:
- https://github.com/emersion/xdg-desktop-portal-wlr
tasks:
Expand Down
2 changes: 2 additions & 0 deletions contrib/config.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[screencast]
output=
18 changes: 18 additions & 0 deletions include/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef CONFIG_H
#define CONFIG_H

#include "logger.h"

struct config_screencast {
char *output_name;
};

struct xdpw_config {
struct config_screencast screencast_conf;
};

void print_config(enum LOGLEVEL loglevel, struct xdpw_config *config);
void finish_config(struct xdpw_config *config);
void init_config(char *configfile, struct xdpw_config *config);

#endif
2 changes: 2 additions & 0 deletions include/logger.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <stdio.h>

#define DEFAULT_LOGLEVEL ERROR

enum LOGLEVEL { QUIET, ERROR, WARN, INFO, DEBUG, TRACE };

struct logger_properties {
Expand Down
3 changes: 0 additions & 3 deletions include/screencast_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ struct xdpw_screencast_context {
struct zxdg_output_manager_v1* xdg_output_manager;
struct wl_shm *shm;

// cli options
const char *output_name;

// sessions
struct wl_list screencast_instances;
};
Expand Down
4 changes: 3 additions & 1 deletion include/xdpw.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#endif

#include "screencast_common.h"
#include "config.h"

struct xdpw_state {
struct wl_list xdpw_sessions;
Expand All @@ -21,6 +22,7 @@ struct xdpw_state {
uint32_t screencast_source_types; // bitfield of enum source_types
uint32_t screencast_cursor_modes; // bitfield of enum cursor_modes
uint32_t screencast_version;
struct xdpw_config *config;
};

struct xdpw_request {
Expand All @@ -41,7 +43,7 @@ enum {
};

int xdpw_screenshot_init(struct xdpw_state *state);
int xdpw_screencast_init(struct xdpw_state *state, const char *output_name);
int xdpw_screencast_init(struct xdpw_state *state);

struct xdpw_request *xdpw_request_create(sd_bus *bus, const char *object_path);
void xdpw_request_destroy(struct xdpw_request *req);
Expand Down
7 changes: 7 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ add_project_arguments(cc.get_supported_arguments([
'-D_POSIX_C_SOURCE=200809L',
]), language: 'c')

prefix = get_option('prefix')
sysconfdir = get_option('sysconfdir')
add_project_arguments('-DSYSCONFDIR="@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c')

inc = include_directories('include')

rt = cc.find_library('rt')
pipewire = dependency('libpipewire-0.3', version: '>= 0.3.2')
wayland_client = dependency('wayland-client')
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
iniparser = cc.find_library('iniparser', dirs: [join_paths(get_option('prefix'),get_option('libdir'))])

if get_option('sd-bus-provider') == 'auto'
assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto')
Expand Down Expand Up @@ -59,6 +64,7 @@ executable(
files([
'src/core/main.c',
'src/core/logger.c',
'src/core/config.c',
'src/core/request.c',
'src/core/session.c',
'src/screenshot/screenshot.c',
Expand All @@ -73,6 +79,7 @@ executable(
sdbus,
pipewire,
rt,
iniparser,
],
include_directories: [inc],
install: true,
Expand Down
109 changes: 109 additions & 0 deletions src/core/config.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "config.h"
#include "xdpw.h"
#include "logger.h"

#include <dictionary.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <iniparser.h>

void print_config(enum LOGLEVEL loglevel, struct xdpw_config *config) {
logprint(loglevel, "config: Outputname %s",config->screencast_conf.output_name);
}

// NOTE: calling finish_config won't prepare the config to be read again from config file
// with init_config since to pointers and other values won't be reset to NULL, or 0
void finish_config(struct xdpw_config *config) {
logprint(DEBUG, "config: destroying config");

// screencast
free(&config->screencast_conf.output_name);
}

static void getstring_from_conffile(dictionary *d, char *key, char **dest, char *fallback) {
if (*dest != NULL) {
return;
}
const char *c = iniparser_getstring(d, key, fallback);
if (c == NULL) {
return;
}
// Allow keys without value as default
if (strcmp(c, "") != 0) {
*dest = strdup(c);
} else {
*dest = fallback ? strdup(fallback) : NULL;
}
}

static bool file_exists(const char *path) {
return path && access(path, R_OK) != -1;
}

static char *config_path(char *prefix, char *filename) {
if (!prefix || !prefix[0] || !filename || !filename[0]) {
return NULL;
}

char *config_folder = "xdg-desktop-portal-wlr";

size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename);
char *path = calloc(size, sizeof(char));
snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename);
return path;
}

static void config_parse_file(char *configfile, struct xdpw_config *config) {
dictionary *d = iniparser_load(configfile);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check for a NULL d here, in case iniparser_load failed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, if d is NULL iniparser_get* will just return the fallback.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but if iniparser failed to read the file we want a proper error message

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added logging, but don't know how to supress the log from iniparser. We could check before and skip it when the config file is null, or just have the line in the log.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, which log from iniparser?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is the line
iniparser: cannot open (NIL) or path to file
in stderr if iniparser_load fails.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe don't call iniparser_load if configfile == NULL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved all checks of configfile to the part where we load it with iniparser_load, so we don't have to check for the same things multiple times.


// screencast
getstring_from_conffile(d, "screencast:output_name", &config->screencast_conf.output_name, NULL);

iniparser_freedict(d);
logprint(DEBUG, "config: config file parsed");
print_config(DEBUG, config);
}

static char *get_config_path(void) {
const char *home = getenv("HOME");
size_t size_fallback = 1 + strlen(home) + strlen("/.config");
char *config_home_fallback = calloc(size_fallback, sizeof(char));
snprintf(config_home_fallback, size_fallback, "%s/.config", home);

char *prefix[4];
prefix[0] = getenv("XDG_CONFIG_HOME");
prefix[1] = config_home_fallback;
prefix[2] = SYSCONFDIR "/xdg";
prefix[3] = SYSCONFDIR;

char *config[2];
config[0] = getenv("XDG_CURRENT_DESKTOP");
config[1] = "config";

for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < 2; j++) {
char *path = config_path(prefix[i], config[j]);
if (!path) {
continue;
}
logprint(TRACE, "config: trying config file %s", path);
if (file_exists(path)) {
return path;
}
free(path);
}
}

return NULL;
}

void init_config(char *configfile, struct xdpw_config *config) {
if (configfile == NULL) {
configfile = get_config_path();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This memory is never free'd. This wouldn't be the biggest memory leak in xdpw, that said.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this memory be freed here at the end of main?
Well better not make that a contest ^^'

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment only changes the local configfile variable. The variable in main is unaffected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

damn, yeah screwed that up. Should work now.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should check configfile != NULL at this point. get_config_path can return NULL.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but afaics libiniparser could handle NULL as a path to the config file and will insert the defaults as if this file is empty. If you want to avoid this, I could write another function setting the default values manually.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Owner

@emersion emersion Feb 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(IMHO just set the defaults in main when initializing the config variable)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It happens here https://github.com/ndevilla/iniparser/blob/f858275f7f307eecba84c2f5429483f9f28007f8/src/iniparser.c#L419

About defaulting in main is, i can't detect if something is a default or was set via commandline parameter.

}

logprint(INFO, "config: using config file %s", configfile);
config_parse_file(configfile, config);
}
51 changes: 34 additions & 17 deletions src/core/logger.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,6 @@
#include <string.h>
#include <time.h>

static int NUM_LEVELS = 6;

static const char *loglevels[] = {
"QUIET",
"ERROR",
"WARN",
"INFO",
"DEBUG",
"TRACE"
};

static struct logger_properties logprops;

void init_logger(FILE *dst, enum LOGLEVEL level) {
Expand All @@ -24,16 +13,44 @@ void init_logger(FILE *dst, enum LOGLEVEL level) {
}

enum LOGLEVEL get_loglevel(const char *level) {
int i;
for (i = 0; i < NUM_LEVELS; i++) {
if (!strcmp(level, loglevels[i])) {
return (enum LOGLEVEL) i;
}
if (strcmp(level, "QUIET") == 0) {
return QUIET;
} else if (strcmp(level, "ERROR") == 0) {
return ERROR;
} else if (strcmp(level, "WARN") == 0) {
return WARN;
} else if (strcmp(level, "INFO") == 0) {
return INFO;
} else if (strcmp(level, "DEBUG") == 0) {
return DEBUG;
} else if (strcmp(level, "TRACE") == 0) {
return TRACE;
}

fprintf(stderr, "Could not understand log level %s\n", level);
abort();
}

char *print_loglevel(enum LOGLEVEL loglevel) {
switch (loglevel) {
case QUIET:
return "QUIET";
case ERROR:
return "ERROR";
case WARN:
return "WARN";
case INFO:
return "INFO";
case DEBUG:
return "DEBUG";
case TRACE:
return "TRACE";
}
fprintf(stderr, "Could not find log level %d\n", loglevel);
abort();
return NULL;
}

void logprint(enum LOGLEVEL level, char *msg, ...) {
if (!logprops.dst) {
fprintf(stderr, "Logger has been called, but was not initialized\n");
Expand All @@ -56,7 +73,7 @@ void logprint(enum LOGLEVEL level, char *msg, ...) {

fprintf(logprops.dst, "%s", timestr);
fprintf(logprops.dst, " ");
fprintf(logprops.dst, "[%s]", loglevels[level]);
fprintf(logprops.dst, "[%s]", print_loglevel(level));
fprintf(logprops.dst, " - ");

va_start(args, msg);
Expand Down
21 changes: 16 additions & 5 deletions src/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ static int xdpw_usage(FILE* stream, int rc) {
" QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n"
" -o, --output=<name> Select output to capture.\n"
" metadata (performs no conversion).\n"
" -c, --config=<config file> Select config file.\n"
" (default is $XDG_CONFIG_HOME/xdg-desktop-portal-wlr/config)\n"
" -r, --replace Replace a running instance.\n"
" -h, --help Get help (this text).\n"
"\n";
Expand All @@ -39,14 +41,16 @@ static int handle_name_lost(sd_bus_message *m, void *userdata, sd_bus_error *ret
}

int main(int argc, char *argv[]) {
const char* output_name = NULL;
enum LOGLEVEL loglevel = ERROR;
struct xdpw_config config = {0};
char *configfile = NULL;
enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL;
bool replace = false;

static const char* shortopts = "l:o:rh";
static const char* shortopts = "l:o:c:rh";
static const struct option longopts[] = {
{ "loglevel", required_argument, NULL, 'l' },
{ "output", required_argument, NULL, 'o' },
{ "config", required_argument, NULL, 'c' },
{ "replace", no_argument, NULL, 'r' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
Expand All @@ -62,7 +66,10 @@ int main(int argc, char *argv[]) {
loglevel = get_loglevel(optarg);
break;
case 'o':
output_name = optarg;
config.screencast_conf.output_name = strdup(optarg);
break;
case 'c':
configfile = strdup(optarg);
break;
case 'r':
replace = true;
Expand All @@ -75,6 +82,7 @@ int main(int argc, char *argv[]) {
}

init_logger(stderr, loglevel);
init_config(configfile, &config);

int ret = 0;

Expand Down Expand Up @@ -111,12 +119,13 @@ int main(int argc, char *argv[]) {
.screencast_source_types = MONITOR,
.screencast_cursor_modes = HIDDEN | EMBEDDED,
.screencast_version = XDP_CAST_PROTO_VER,
.config = &config,
};

wl_list_init(&state.xdpw_sessions);

xdpw_screenshot_init(&state);
ret = xdpw_screencast_init(&state, output_name);
ret = xdpw_screencast_init(&state);
if (ret < 0) {
logprint(ERROR, "xdpw: failed to initialize screencast");
goto error;
Expand Down Expand Up @@ -217,6 +226,8 @@ int main(int argc, char *argv[]) {
}

// TODO: cleanup
finish_config(&config);
free(configfile);

return EXIT_SUCCESS;

Expand Down
Loading