ACL/DVR: Rewrite channel tag and add DVR config selection

- remove also obsolete only_tag
- accept only uuid for DVR entry config name
- try to lock DVR entry when used in dvr_thread
This commit is contained in:
Jaroslav Kysela 2014-09-02 09:44:52 +02:00
parent 3c64f87d96
commit 7fdf5f1f12
16 changed files with 479 additions and 149 deletions

View file

@ -68,13 +68,11 @@ The columns have the following functions:
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt>All Configs (VR)
<dt>DVR Config Profile
<dd>
Allow to use all DVR configuration profiles. If not set, a DVR
configuration profile matched to the authorized user by name is always used
(the configuration profile must have same name as the user). If the DVR
configuration profile does not exists, the default profile is used.
The user is also not allowed to select another profile.
If set, the user will only be able to use the DVR config profile
equal to this value.
Note that this field is unset when the DVR Config Profile is removed.
<dt>Web interface
<dd>
@ -84,13 +82,6 @@ The columns have the following functions:
<dd>
Enables access to the Configuration tab.
<dt>Username Channel Tag Match
<dd>
If enabled, the user will only be able to access channels with a tag the
same name as the username.
This provides a very rudimentary way of limiting access to certain channels.
<dt>Min Channel Num
<dd>
If nonzero, the user will only be able to access channels with
@ -103,11 +94,9 @@ The columns have the following functions:
<dt>Channel Tag
<dd>
If set, the user will only be able to access channels with
a channel tag equal to this value. Note that this field stores
the tag name (not identified). It means that this field would not be
updated when the tag name is changed. A manual re-set of this field
is required (for security reasons).
If set, the user will only be able to access channels containing
this channel tag.
Note that this field is unset when the channel tag is removed.
<dt>Comment
<dd>

View file

@ -37,6 +37,7 @@
#include "access.h"
#include "settings.h"
#include "channels.h"
#include "dvr/dvr.h"
#include "tcp.h"
struct access_entry_queue access_entries;
@ -161,6 +162,7 @@ access_destroy(access_t *a)
{
free(a->aa_username);
free(a->aa_representative);
htsmsg_destroy(a->aa_dvrcfgs);
htsmsg_destroy(a->aa_chtags);
free(a);
}
@ -295,10 +297,16 @@ access_update(access_t *a, access_entry_t *ae)
}
}
if(ae->ae_chtag && ae->ae_chtag[0] != '\0') {
if(ae->ae_dvr_config && ae->ae_dvr_config->dvr_config_name[0] != '\0') {
if (a->aa_dvrcfgs == NULL)
a->aa_dvrcfgs = htsmsg_create_list();
htsmsg_add_str(a->aa_dvrcfgs, NULL, idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id));
}
if(ae->ae_chtag && ae->ae_chtag->ct_name[0] != '\0') {
if (a->aa_chtags == NULL)
a->aa_chtags = htsmsg_create_list();
htsmsg_add_str(a->aa_chtags, NULL, ae->ae_chtag);
htsmsg_add_str(a->aa_chtags, NULL, idnode_uuid_as_str(&ae->ae_chtag->ct_id));
}
a->aa_rights |= ae->ae_rights;
@ -557,12 +565,8 @@ access_entry_update_rights(access_entry_t *ae)
r |= ACCESS_ADVANCED_STREAMING;
if (ae->ae_dvr)
r |= ACCESS_RECORDER;
if (ae->ae_dvrallcfg)
r |= ACCESS_RECORDER_ALL;
if (ae->ae_webui)
r |= ACCESS_WEB_INTERFACE;
if (ae->ae_tag_only)
r |= ACCESS_TAG_ONLY;
if (ae->ae_admin)
r |= ACCESS_ADMIN;
ae->ae_rights = r;
@ -639,6 +643,11 @@ access_entry_destroy(access_entry_t *ae)
TAILQ_REMOVE(&access_entries, ae, ae_link);
idnode_unlink(&ae->ae_id);
if (ae->ae_dvr_config)
LIST_REMOVE(ae, ae_dvr_config_link);
if (ae->ae_chtag)
LIST_REMOVE(ae, ae_channel_tag_link);
while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL)
{
TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link);
@ -653,6 +662,38 @@ access_entry_destroy(access_entry_t *ae)
free(ae);
}
/*
*
*/
void
access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
{
access_entry_t *ae;
while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) {
LIST_REMOVE(ae, ae_dvr_config_link);
ae->ae_dvr_config = NULL;
if (delconf)
access_entry_save(ae);
}
}
/*
*
*/
void
access_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
{
access_entry_t *ae;
while ((ae = LIST_FIRST(&ct->ct_accesses)) != NULL) {
LIST_REMOVE(ae, ae_channel_tag_link);
ae->ae_chtag = NULL;
if (delconf)
access_entry_save(ae);
}
}
/**
*
*/
@ -806,14 +847,66 @@ access_entry_class_password2_set(void *o, const void *v)
return 0;
}
static htsmsg_t *
access_entry_chtag_list ( void *o )
static int
access_entry_chtag_set(void *o, const void *v)
{
channel_tag_t *ct;
htsmsg_t *m = htsmsg_create_list();
TAILQ_FOREACH(ct, &channel_tags, ct_link)
htsmsg_add_str(m, NULL, ct->ct_name);
return m;
access_entry_t *ae = (access_entry_t *)o;
channel_tag_t *tag = v ? channel_tag_find_by_uuid(v) : NULL;
if (tag == NULL && ae->ae_chtag) {
LIST_REMOVE(ae, ae_channel_tag_link);
ae->ae_chtag = NULL;
return 1;
} else if (ae->ae_chtag != tag) {
if (ae->ae_chtag)
LIST_REMOVE(ae, ae_channel_tag_link);
ae->ae_chtag = tag;
LIST_INSERT_HEAD(&tag->ct_accesses, ae, ae_channel_tag_link);
return 1;
}
return 0;
}
static const void *
access_entry_chtag_get(void *o)
{
static const char *ret;
access_entry_t *ae = (access_entry_t *)o;
if (ae->ae_chtag)
ret = idnode_uuid_as_str(&ae->ae_chtag->ct_id);
else
ret = "";
return &ret;
}
static int
access_entry_dvr_config_set(void *o, const void *v)
{
access_entry_t *ae = (access_entry_t *)o;
dvr_config_t *cfg = v ? dvr_config_find_by_uuid(v) : NULL;
if (cfg == NULL && ae->ae_dvr_config) {
LIST_REMOVE(ae, ae_dvr_config_link);
ae->ae_dvr_config = NULL;
return 1;
} else if (ae->ae_dvr_config != cfg) {
if (ae->ae_dvr_config)
LIST_REMOVE(ae, ae_dvr_config_link);
ae->ae_dvr_config = cfg;
LIST_INSERT_HEAD(&cfg->dvr_accesses, ae, ae_dvr_config_link);
return 1;
}
return 0;
}
static const void *
access_entry_dvr_config_get(void *o)
{
static const char *ret;
access_entry_t *ae = (access_entry_t *)o;
if (ae->ae_dvr_config)
ret = idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id);
else
ret = "";
return &ret;
}
const idclass_t access_entry_class = {
@ -886,10 +979,13 @@ const idclass_t access_entry_class = {
.off = offsetof(access_entry_t, ae_dvr),
},
{
.type = PT_BOOL,
.id = "dvrallcfg",
.name = "All Configs (VR)",
.off = offsetof(access_entry_t, ae_dvrallcfg),
.type = PT_STR,
.id = "dvr_config",
.name = "DVR Config Profile",
.set = access_entry_dvr_config_set,
.get = access_entry_dvr_config_get,
.list = dvr_entry_class_config_name_list,
.off = offsetof(access_entry_t, ae_dvr_config),
},
{
.type = PT_BOOL,
@ -903,12 +999,6 @@ const idclass_t access_entry_class = {
.name = "Admin",
.off = offsetof(access_entry_t, ae_admin),
},
{
.type = PT_BOOL,
.id = "tag_only",
.name = "Username Channel Tag Match",
.off = offsetof(access_entry_t, ae_tag_only),
},
{
.type = PT_U32,
.id = "channel_min",
@ -926,7 +1016,9 @@ const idclass_t access_entry_class = {
.id = "channel_tag",
.name = "Channel Tag",
.off = offsetof(access_entry_t, ae_chtag),
.list = access_entry_chtag_list,
.set = access_entry_chtag_set,
.get = access_entry_chtag_get,
.list = channel_tag_class_get_list,
},
{
.type = PT_STR,

View file

@ -22,6 +22,9 @@
#include "idnode.h"
#include "htsmsg.h"
struct dvr_config;
struct channel_tag;
typedef struct access_ipmask {
TAILQ_ENTRY(access_ipmask) ai_link;
@ -48,18 +51,25 @@ typedef struct access_entry {
char *ae_password;
char *ae_password2;
char *ae_comment;
int ae_index;
int ae_enabled;
int ae_streaming;
int ae_adv_streaming;
int ae_dvr;
int ae_dvrallcfg;
struct dvr_config *ae_dvr_config;
LIST_ENTRY(access_entry) ae_dvr_config_link;
int ae_webui;
int ae_admin;
int ae_tag_only;
uint32_t ae_chmin;
uint32_t ae_chmax;
char *ae_chtag;
struct channel_tag *ae_chtag;
LIST_ENTRY(access_entry) ae_channel_tag_link;
uint32_t ae_rights;
@ -85,6 +95,7 @@ typedef struct access {
char *aa_username;
char *aa_representative;
uint32_t aa_rights;
htsmsg_t *aa_dvrcfgs;
uint32_t aa_chmin;
uint32_t aa_chmax;
htsmsg_t *aa_chtags;
@ -96,14 +107,11 @@ typedef struct access {
#define ACCESS_ADVANCED_STREAMING (1<<1)
#define ACCESS_WEB_INTERFACE (1<<2)
#define ACCESS_RECORDER (1<<3)
#define ACCESS_RECORDER_ALL (1<<4)
#define ACCESS_TAG_ONLY (1<<5)
#define ACCESS_ADMIN (1<<6)
#define ACCESS_ADMIN (1<<4)
#define ACCESS_FULL \
(ACCESS_STREAMING | ACCESS_ADVANCED_STREAMING | \
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | \
ACCESS_RECORDER_ALL | ACCESS_ADMIN)
ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
/**
* Create a new ticket for the requested resource and generate a id for it
@ -165,6 +173,14 @@ access_entry_create(const char *uuid, htsmsg_t *conf);
void
access_entry_save(access_entry_t *ae);
/**
*
*/
void
access_destroy_by_dvr_config(struct dvr_config *cfg, int delconf);
void
access_destroy_by_channel_tag(struct channel_tag *ct, int delconf);
/**
*
*/

View file

@ -25,21 +25,28 @@
static const char *
api_dvr_config_name( access_t *perm, const char *config_uuid )
{
dvr_config_t *cfg;
dvr_config_t *cfg = NULL;
htsmsg_field_t *f;
const char *uuid;
lock_assert(&global_lock);
if (access_verify2(perm, ACCESS_RECORDER_ALL))
if (perm->aa_dvrcfgs == NULL)
return config_uuid; /* no change */
cfg = dvr_config_find_by_name(perm->aa_username);
if (cfg)
return cfg->dvr_config_name;
config_uuid = config_uuid ?: "";
HTSMSG_FOREACH(f, perm->aa_dvrcfgs) {
uuid = htsmsg_field_get_str(f) ?: "";
if (strcmp(uuid, config_uuid) == 0)
return config_uuid;
if (!cfg)
cfg = dvr_config_find_by_uuid(uuid);
}
if (perm->aa_username)
tvhlog(LOG_INFO, "dvr", "User '%s' has no dvr config with identical name, using default...", perm->aa_username);
if (!cfg && perm->aa_username)
tvhlog(LOG_INFO, "dvr", "User '%s' has no valid dvr config in ACL, using default...", perm->aa_username);
return NULL; /* default */
return cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL;
}
/*
@ -71,9 +78,6 @@ api_dvr_config_create
return EINVAL;
if (s[0] == '\0')
return EINVAL;
if (access_verify2(perm, ACCESS_ADMIN) &&
access_verify2(perm, ACCESS_RECORDER_ALL | ACCESS_RECORDER))
return EACCES;
pthread_mutex_lock(&global_lock);
if ((cfg = dvr_config_create(NULL, NULL, conf)))
@ -153,16 +157,20 @@ api_dvr_entry_create
{
dvr_entry_t *de;
htsmsg_t *conf;
const char *s1, *s2;
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
if (access_verify2(perm, ACCESS_RECORDER_ALL)) {
pthread_mutex_lock(&global_lock);
s1 = htsmsg_get_str(conf, "config_name");
s2 = api_dvr_config_name(perm, s1);
if (strcmp(s1 ?: "", s2 ?: "")) {
htsmsg_delete_field(conf, "config_name");
htsmsg_add_str(conf, "config_name", perm->aa_username ?: "");
if (s2)
htsmsg_add_str(conf, "config_name", s2);
}
pthread_mutex_lock(&global_lock);
if ((de = dvr_entry_create(NULL, conf)))
dvr_entry_save(de);
pthread_mutex_unlock(&global_lock);

View file

@ -163,19 +163,6 @@ channel_class_tags_set ( void *obj, const void *p )
return channel_set_tags_by_list(obj, (htsmsg_t*)p);
}
static htsmsg_t *
channel_class_tags_enum ( void *obj )
{
htsmsg_t *e, *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channeltag/list");
htsmsg_add_str(m, "event", "channeltag");
e = htsmsg_create_map();
htsmsg_add_bool(e, "enum", 1);
htsmsg_add_msg(m, "params", e);
return m;
}
static void
channel_class_icon_notify ( void *obj )
{
@ -369,7 +356,7 @@ const idclass_t channel_class = {
.name = "Tags",
.get = channel_class_tags_get,
.set = channel_class_tags_set,
.list = channel_class_tags_enum,
.list = channel_tag_class_get_list,
.rend = channel_class_tags_rend
},
{}
@ -430,7 +417,8 @@ channel_access(channel_t *ch, access_t *a, const char *username)
htsmsg_field_t *f;
HTSMSG_FOREACH(f, a->aa_chtags) {
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
if (!strcmp(htsmsg_field_get_str(f) ?: "", ctm->ctm_tag->ct_name))
if (!strcmp(htsmsg_field_get_str(f) ?: "",
idnode_uuid_as_str(&ctm->ctm_tag->ct_id)))
goto chtags_ok;
}
}
@ -438,17 +426,6 @@ channel_access(channel_t *ch, access_t *a, const char *username)
}
chtags_ok:
/* Channel tag <-> user name match */
if (ch && (a->aa_rights & ACCESS_TAG_ONLY) != 0) {
channel_tag_mapping_t *ctm;
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
if (!strcmp(username ?: "", ctm->ctm_tag->ct_name))
goto tagonly_ok;
}
return 0;
}
tagonly_ok:
return 1;
}
@ -761,6 +738,9 @@ channel_tag_create(const char *uuid, htsmsg_t *conf)
channel_tag_t *ct;
ct = calloc(1, sizeof(channel_tag_t));
LIST_INIT(&ct->ct_ctms);
LIST_INIT(&ct->ct_autorecs);
LIST_INIT(&ct->ct_accesses);
if (idnode_insert(&ct->ct_id, uuid, &channel_tag_class, IDNODE_SHORT_UUID)) {
if (uuid)
@ -807,6 +787,9 @@ channel_tag_destroy(channel_tag_t *ct, int delconf)
TAILQ_REMOVE(&channel_tags, ct, ct_link);
idnode_unlink(&ct->ct_id);
autorec_destroy_by_channel_tag(ct, delconf);
access_destroy_by_channel_tag(ct, delconf);
free(ct->ct_name);
free(ct->ct_comment);
free(ct->ct_icon);
@ -849,6 +832,17 @@ channel_tag_class_get_title (idnode_t *self)
return ct->ct_name ?: "";
}
/* exported for others */
htsmsg_t *
channel_tag_class_get_list(void *o)
{
htsmsg_t *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channeltag/list");
htsmsg_add_str(m, "event", "channeltag");
return m;
}
const idclass_t channel_tag_class = {
.ic_class = "channeltag",
.ic_caption = "Channel Tag",

View file

@ -95,6 +95,8 @@ typedef struct channel_tag {
struct dvr_autorec_entry_list ct_autorecs;
struct access_entry_list ct_accesses;
int ct_htsp_id;
} channel_tag_t;
@ -164,6 +166,8 @@ static inline channel_tag_t *channel_tag_find_by_uuid(const char *uuid)
void channel_tag_save(channel_tag_t *ct);
htsmsg_t * channel_tag_class_get_list(void *o);
int channel_access(channel_t *ch, struct access *a, const char *username);
int channel_tag_map(channel_t *ch, channel_tag_t *ct);

View file

@ -853,6 +853,109 @@ config_migrate_v10 ( void )
config_migrate_move("channeltags", "channel/tag");
}
static const char *
config_find_uuid( htsmsg_t *map, const char *name, const char *value )
{
htsmsg_t *e;
htsmsg_field_t *f;
const char *s;
HTSMSG_FOREACH(f, map) {
if (!(e = htsmsg_field_get_map(f))) continue;
if ((s = htsmsg_get_str(e, name)) != NULL) {
if (!strcmp(s, value))
return f->hmf_name;
}
}
return NULL;
}
static void
config_modify_acl_dvallcfg( htsmsg_t *c, htsmsg_t *dvr_config )
{
uint32_t a;
const char *username, *uuid;
username = htsmsg_get_str(c, "username");
if (!htsmsg_get_u32(c, "dvallcfg", &a))
if (a == 0) {
uuid = username ? config_find_uuid(dvr_config, "name", username) : NULL;
if (uuid)
htsmsg_add_str(c, "dvr_config", uuid);
}
htsmsg_delete_field(c, "dvallcfg");
}
static void
config_modify_acl_tag_only( htsmsg_t *c, htsmsg_t *channel_tag )
{
uint32_t a;
const char *username, *tag, *uuid;
username = htsmsg_get_str(c, "username");
tag = htsmsg_get_str(c, "channel_tag");
if (!tag || tag[0] == '\0')
tag = NULL;
if (tag == NULL && !htsmsg_get_u32(c, "tag_only", &a)) {
if (a) {
uuid = username ? config_find_uuid(channel_tag, "name", username) : NULL;
if (uuid)
htsmsg_add_str(c, "channel_tag", uuid);
}
} else if (tag) {
uuid = config_find_uuid(channel_tag, "name", tag);
if (uuid) {
htsmsg_delete_field(c, "channel_tag");
htsmsg_add_str(c, "channel_tag", uuid);
}
}
htsmsg_delete_field(c, "tag_only");
}
static void
config_modify_dvr_config_name( htsmsg_t *c, htsmsg_t *dvr_config )
{
const char *config_name, *uuid;
config_name = htsmsg_get_str(c, "config_name");
uuid = config_name ? config_find_uuid(dvr_config, "name", config_name) : NULL;
htsmsg_delete_field(c, "config_name");
htsmsg_add_str(c, "config_name", uuid ?: "");
}
static void
config_migrate_v11 ( void )
{
htsmsg_t *dvr_config;
htsmsg_t *channel_tag;
htsmsg_t *c, *e;
htsmsg_field_t *f;
dvr_config = hts_settings_load("dvr/config");
channel_tag = hts_settings_load("channel/tag");
if ((c = hts_settings_load("accesscontrol")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
config_modify_acl_dvallcfg(e, dvr_config);
config_modify_acl_tag_only(e, channel_tag);
}
htsmsg_destroy(c);
}
if ((c = hts_settings_load("dvr/log")) != NULL) {
HTSMSG_FOREACH(f, c) {
if (!(e = htsmsg_field_get_map(f))) continue;
config_modify_dvr_config_name(e, dvr_config);
}
htsmsg_destroy(c);
}
htsmsg_destroy(channel_tag);
htsmsg_destroy(dvr_config);
}
/*
* Migration table
*/
@ -867,6 +970,7 @@ static const config_migrate_t config_migrate_table[] = {
config_migrate_v8,
config_migrate_v9,
config_migrate_v10,
config_migrate_v11
};
/*

View file

@ -28,6 +28,7 @@
typedef struct dvr_config {
idnode_t dvr_id;
LIST_ENTRY(dvr_config) config_link;
int dvr_enabled;
@ -72,6 +73,10 @@ typedef struct dvr_config {
/* Duplicate detect */
int dvr_dup_detect_episode;
struct dvr_entry_list dvr_entries;
struct access_entry_list dvr_accesses;
} dvr_config_t;
extern struct dvr_config_list dvrconfigs;
@ -150,7 +155,7 @@ typedef struct dvr_entry {
*/
dvr_config_t *de_config;
char *de_config_name;
LIST_ENTRY(dvr_entry) de_config_link;
time_t de_start;
time_t de_stop;
@ -185,6 +190,7 @@ typedef struct dvr_entry {
* Recording state (onyl valid if de_sched_state == DVR_RECORDING)
*/
dvr_rs_state_t de_rec_state;
int de_locked;
/**
* Number of errors (only to be modified by the recording thread)
@ -346,6 +352,7 @@ dvr_entry_update( dvr_entry_t *de,
time_t de_start_extra, time_t de_stop_extra );
void dvr_init(void);
void dvr_config_init(void);
void dvr_done(void);
@ -438,6 +445,8 @@ void dvr_autorec_check_serieslink(epg_serieslink_t *s);
void autorec_destroy_by_channel(channel_t *ch, int delconf);
void autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf);
/**
*
*/

View file

@ -392,18 +392,6 @@ dvr_autorec_entry_class_tag_get(void *o)
return &ret;
}
static htsmsg_t *
dvr_autorec_entry_class_tag_list(void *o)
{
htsmsg_t *e, *m = htsmsg_create_map();
htsmsg_add_str(m, "type", "api");
htsmsg_add_str(m, "uri", "channeltag/list");
htsmsg_add_str(m, "event", "channeltag");
e = htsmsg_create_map();
htsmsg_add_msg(m, "params", e);
return m;
}
static int
dvr_autorec_entry_class_time_set(void *o, const void *v, int *tm)
{
@ -726,7 +714,7 @@ const idclass_t dvr_autorec_entry_class = {
.name = "Channel Tag",
.set = dvr_autorec_entry_class_tag_set,
.get = dvr_autorec_entry_class_tag_get,
.list = dvr_autorec_entry_class_tag_list,
.list = channel_tag_class_get_list,
},
{
.type = PT_STR,
@ -913,7 +901,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
CHANNEL_FOREACH(ch) {
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e))
dvr_entry_create_by_autorec(e, dae);
dvr_entry_create_by_autorec(e, dae);
}
}
}
@ -936,3 +924,25 @@ autorec_destroy_by_channel(channel_t *ch, int delconf)
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("autorec", m);
}
/*
*
*/
void
autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
{
dvr_autorec_entry_t *dae;
htsmsg_t *m;
while((dae = LIST_FIRST(&ct->ct_autorecs)) != NULL) {
LIST_REMOVE(dae, dae_channel_tag_link);
dae->dae_channel_tag = NULL;
if (delconf)
dvr_autorec_save(dae);
}
/* Notify web clients that we have messed with the tables */
m = htsmsg_create_map();
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("autorec", m);
}

View file

@ -43,7 +43,7 @@ struct dvr_entry_list dvrentries;
static gtimer_t dvr_dbus_timer;
#endif
static void dvr_entry_remove(dvr_entry_t *de, int delconf);
static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
static void dvr_timer_expire(void *aux);
static void dvr_timer_start_recording(void *aux);
static void dvr_timer_stop_recording(void *aux);
@ -421,7 +421,7 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
if(de2 != de &&
de2->de_start == de->de_start &&
de2->de_sched_state != DVR_COMPLETED) {
dvr_entry_remove(de, 0);
dvr_entry_destroy(de, 0);
return NULL;
}
}
@ -524,7 +524,11 @@ dvr_entry_create_htsp(const char *config_uuid,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri)
{
return _dvr_entry_create(config_uuid, NULL,
dvr_config_t *cfg = dvr_config_find_by_uuid(config_uuid);
if (!cfg)
cfg = dvr_config_find_by_name(config_uuid);
return _dvr_entry_create(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
NULL,
ch, start, stop, start_extra, stop_extra,
title, description, lang, content_type,
creator, dae, pri);
@ -625,8 +629,10 @@ dvr_entry_dec_ref(dvr_entry_t *de)
if(de->de_autorec != NULL)
LIST_REMOVE(de, de_autorec_link);
if(de->de_config != NULL)
LIST_REMOVE(de, de_config_link);
free(de->de_filename);
free(de->de_config_name);
free(de->de_creator);
if (de->de_title) lang_str_destroy(de->de_title);
if (de->de_desc) lang_str_destroy(de->de_desc);
@ -639,7 +645,7 @@ dvr_entry_dec_ref(dvr_entry_t *de)
*
*/
static void
dvr_entry_remove(dvr_entry_t *de, int delconf)
dvr_entry_destroy(dvr_entry_t *de, int delconf)
{
if (delconf)
hts_settings_remove("dvr/log/%s", idnode_uuid_as_str(&de->de_id));
@ -665,6 +671,27 @@ dvr_entry_remove(dvr_entry_t *de, int delconf)
dvr_entry_dec_ref(de);
}
/**
*
*/
static void
dvr_entry_destroy_by_config(dvr_config_t *cfg, int delconf)
{
dvr_entry_t *de;
dvr_config_t *def = NULL;
while ((de = LIST_FIRST(&cfg->dvr_entries)) != NULL) {
LIST_REMOVE(de, de_config_link);
if (!def)
def = dvr_config_find_by_name_default("");
de->de_config = def;
if (def)
LIST_INSERT_HEAD(&def->dvr_entries, de, de_config_link);
if (delconf)
dvr_entry_save(de);
}
}
/**
*
*/
@ -707,7 +734,7 @@ static void
dvr_timer_expire(void *aux)
{
dvr_entry_t *de = aux;
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
}
@ -718,6 +745,9 @@ static dvr_entry_t *_dvr_entry_update
{
int save = 0;
if (de->de_locked)
return de;
/* Start/Stop */
if (e) {
start = e->start;
@ -836,7 +866,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
/* If this was craeted by autorec - just remove it, it'll get recreated */
if (de->de_autorec) {
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
/* Find match */
} else {
@ -1006,7 +1036,7 @@ dvr_entry_cancel(dvr_entry_t *de)
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
return NULL;
case DVR_RECORDING:
@ -1015,11 +1045,11 @@ dvr_entry_cancel(dvr_entry_t *de)
return de;
case DVR_COMPLETED:
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
return NULL;
case DVR_MISSED_TIME:
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
return NULL;
default:
@ -1052,7 +1082,10 @@ dvr_entry_class_save(idnode_t *self)
static void
dvr_entry_class_delete(idnode_t *self)
{
dvr_entry_remove((dvr_entry_t *)self, 1);
dvr_entry_t *de = (dvr_entry_t *)self;
if (de->de_locked)
return;
dvr_entry_destroy(de, 1);
}
static const char *
@ -1070,25 +1103,52 @@ static int
dvr_entry_class_config_name_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
const char *s = v ?: "";
if (strcmp(s, de->de_config_name ?: "")) {
free(de->de_config_name);
de->de_config_name = strdup(v);
de->de_config = dvr_config_find_by_name_default(de->de_config_name);
dvr_config_t *cfg;
if (de->de_locked)
return 0;
cfg = v ? dvr_config_find_by_uuid(v) : NULL;
if (!cfg)
cfg = dvr_config_find_by_name_default("");
if (cfg == NULL) {
if (de->de_config) {
LIST_REMOVE(de, de_config_link);
de->de_config = NULL;
return 1;
}
} else if (de->de_config != cfg) {
if (de->de_config)
LIST_REMOVE(de, de_config_link);
de->de_config = cfg;
LIST_INSERT_HEAD(&cfg->dvr_entries, de, de_config_link);
return 1;
}
/* for sure */
de->de_config = dvr_config_find_by_name_default(de->de_config_name);
return 0;
}
static const void *
dvr_entry_class_config_name_get(void *o)
{
static const char *ret;
dvr_entry_t *de = (dvr_entry_t *)o;
if (de->de_config)
ret = idnode_uuid_as_str(&de->de_config->dvr_id);
else
ret = "";
return &ret;
}
static int
dvr_entry_class_channel_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
channel_t *ch = v ? channel_find_by_uuid(v) : NULL;
if (!de->de_config_name)
dvr_entry_class_config_name_set(o, "");
channel_t *ch;
if (de->de_locked)
return 0;
ch = v ? channel_find_by_uuid(v) : NULL;
if (!de->de_config)
de->de_config = dvr_config_find_by_name_default("");
if (ch == NULL) {
if (de->de_channel) {
LIST_REMOVE(de, de_channel_link);
@ -1134,8 +1194,10 @@ dvr_entry_class_channel_name_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
channel_t *ch;
if (!de->de_config_name)
dvr_entry_class_config_name_set(o, "");
if (de->de_locked)
return 0;
if (!de->de_config)
de->de_config = dvr_config_find_by_name_default("");
if (!strcmp(de->de_channel_name ?: "", v ?: ""))
return 0;
ch = v ? channel_find_by_name(v) : NULL;
@ -1629,8 +1691,8 @@ const idclass_t dvr_entry_class = {
.id = "config_name",
.name = "DVR Configuration",
.set = dvr_entry_class_config_name_set,
.get = dvr_entry_class_config_name_get,
.list = dvr_entry_class_config_name_list,
.off = offsetof(dvr_entry_t, de_config_name),
},
{
.type = PT_STR,
@ -1813,6 +1875,8 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
name = "";
cfg = calloc(1, sizeof(dvr_config_t));
LIST_INIT(&cfg->dvr_entries);
LIST_INIT(&cfg->dvr_accesses);
if (idnode_insert(&cfg->dvr_id, uuid, &dvr_config_class, 0)) {
if (uuid)
@ -1872,6 +1936,10 @@ dvr_config_destroy(dvr_config_t *cfg, int delconf)
}
LIST_REMOVE(cfg, config_link);
idnode_unlink(&cfg->dvr_id);
dvr_entry_destroy_by_config(cfg, delconf);
access_destroy_by_dvr_config(cfg, delconf);
free(cfg->dvr_charset_id);
free(cfg->dvr_charset);
free(cfg->dvr_storage);
@ -1962,12 +2030,23 @@ static int
dvr_config_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_config_t *cfg = (dvr_config_t *)self;
htsmsg_field_t *f;
const char *uuid, *my_uuid;
if (access_verify2(a, ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
if (access_verify2(a, ACCESS_RECORDER_ALL))
return 0;
if (a->aa_dvrcfgs) {
my_uuid = idnode_uuid_as_str(&cfg->dvr_id);
HTSMSG_FOREACH(f, a->aa_dvrcfgs) {
uuid = htsmsg_field_get_str(f) ?: "";
if (!strcmp(uuid, my_uuid))
goto fine;
}
return -1;
}
fine:
if (strcmp(cfg->dvr_config_name ?: "", a->aa_username ?: ""))
return -1;
return 0;
@ -2433,7 +2512,7 @@ dvr_entry_delete(dvr_entry_t *de)
}
}
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
}
/**
@ -2444,7 +2523,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
break;
case DVR_RECORDING:
@ -2455,7 +2534,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
break;
case DVR_MISSED_TIME:
dvr_entry_remove(de, 1);
dvr_entry_destroy(de, 1);
break;
default:
@ -2467,7 +2546,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
*
*/
void
dvr_init(void)
dvr_config_init(void)
{
htsmsg_t *m, *l;
htsmsg_field_t *f;
@ -2520,7 +2599,11 @@ dvr_init(void)
cfg->dvr_config_name, cfg->dvr_storage);
}
}
}
void
dvr_init(void)
{
#if ENABLE_INOTIFY
dvr_inotify_init();
#endif
@ -2542,10 +2625,10 @@ dvr_done(void)
dvr_inotify_done();
#endif
pthread_mutex_lock(&global_lock);
while ((de = LIST_FIRST(&dvrentries)) != NULL)
dvr_entry_destroy(de, 0);
while ((cfg = LIST_FIRST(&dvrconfigs)) != NULL)
dvr_config_destroy(cfg, 0);
while ((de = LIST_FIRST(&dvrentries)) != NULL)
dvr_entry_remove(de, 0);
pthread_mutex_unlock(&global_lock);
dvr_autorec_done();
}

View file

@ -66,6 +66,9 @@ dvr_rec_subscribe(dvr_entry_t *de)
int flags;
assert(de->de_s == NULL);
assert(!de->de_locked);
de->de_locked = 1;
if(de->de_pri < 5)
weight = prio2weight[de->de_pri];
@ -117,6 +120,8 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
globalheaders_destroy(de->de_gh);
de->de_last_error = stopcode;
de->de_locked = 0;
}
@ -177,7 +182,10 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
struct stat st;
char *filename, *s;
struct tm tm;
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
dvr_config_t *cfg = de->de_config;
if (de == NULL)
return -1;
strncpy(path, cfg->dvr_storage, sizeof(path));
path[sizeof(path)-1] = '\0';
@ -304,9 +312,14 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
const source_info_t *si = &ss->ss_si;
const streaming_start_component_t *ssc;
int i;
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
dvr_config_t *cfg = de->de_config;
muxer_container_type_t mc;
if (!cfg) {
dvr_rec_fatal_error(de, "Unable to determine config profile");
return -1;
}
mc = dvr_entry_get_mc(de);
de->de_mux = muxer_create(mc, &cfg->dvr_muxcnf);
@ -427,7 +440,7 @@ static void *
dvr_thread(void *aux)
{
dvr_entry_t *de = aux;
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
dvr_config_t *cfg = de->de_config;
streaming_queue_t *sq = &de->de_sq;
streaming_message_t *sm;
th_pkt_t *pkt;
@ -666,7 +679,7 @@ dvr_thread_epilog(dvr_entry_t *de)
muxer_destroy(de->de_mux);
de->de_mux = NULL;
dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
if(cfg->dvr_postproc && de->de_filename)
dvr_config_t *cfg = de->de_config;
if(cfg && cfg->dvr_postproc && de->de_filename)
dvr_spawn_postproc(de,cfg->dvr_postproc);
}

View file

@ -652,7 +652,6 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
htsmsg_t *out = htsmsg_create_map();
const char *s = NULL, *error = NULL;
const char *p;
dvr_config_t *cfg;
htsmsg_add_u32(out, "id", idnode_get_short_uuid(&de->de_id));
if (de->de_channel)
@ -666,11 +665,9 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
if( de->de_desc && (s = lang_str_get(de->de_desc, NULL)))
htsmsg_add_str(out, "description", s);
if( de->de_filename && de->de_config_name ) {
if ((cfg = dvr_config_find_by_name_default(de->de_config_name))) {
if ((p = tvh_strbegins(de->de_filename, cfg->dvr_storage)))
htsmsg_add_str(out, "path", p);
}
if( de->de_filename && de->de_config ) {
if ((p = tvh_strbegins(de->de_filename, de->de_config->dvr_storage)))
htsmsg_add_str(out, "path", p);
}
switch(de->de_sched_state) {

View file

@ -227,7 +227,7 @@ idnode_get_short_uuid (const idnode_t *in)
const char *
idnode_uuid_as_str(const idnode_t *in)
{
static tvh_uuid_t ret[16];
static tvh_uuid_t __thread ret[16];
static uint8_t p = 0;
bin2hex(ret[p].hex, sizeof(ret[p].hex), in->in_uuid, sizeof(in->in_uuid));
const char *s = ret[p].hex;

View file

@ -821,6 +821,8 @@ main(int argc, char **argv)
subscription_init();
dvr_config_init();
access_init(opt_firstrun, opt_noacl);
#if ENABLE_TIMESHIFT

View file

@ -177,6 +177,7 @@ void gtimer_disarm(gtimer_t *gti);
/*
* List / Queue header declarations
*/
LIST_HEAD(access_entry_list, access_entry);
LIST_HEAD(th_subscription_list, th_subscription);
LIST_HEAD(dvr_config_list, dvr_config);
LIST_HEAD(dvr_entry_list, dvr_entry);

View file

@ -12,6 +12,10 @@ tvheadend.acleditor = function(panel)
items: []
});
var list = 'enabled,username,password,prefix,streaming,adv_streaming,' +
'dvr,dvr_config,webui,admin,channel_min,channel_max,channel_tag,' +
'comment';
tvheadend.idnode_grid(panel, {
url: 'api/access/entry',
comet: 'acl_entries',
@ -20,10 +24,14 @@ tvheadend.acleditor = function(panel)
tabIndex: 0,
add: {
url: 'api/access/entry',
params: {
list: list,
},
create: { }
},
del: true,
move: true,
list: list,
help: function() {
new tvheadend.help('Access Control Entries', 'config_access.html');
},