<dd>
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified.
+ <dt><b>Connection Limit Type</b>
+ <dd>
+ Allow to restrict the limit for connections to streaming or DVR only.
+
<dt><b>Limit Connections</b>
<dd>
If set, this will limit the number of concurrent streaming connections and
int first;
tvh_strlcatf(buf, sizeof(buf), l,
- "%s:%s [%c%c%c%c%c%c%c%c%c%c], conn=%u%s",
+ "%s:%s [%c%c%c%c%c%c%c%c%c%c], conn=%u:s%u:r%u%s",
a->aa_representative ?: "<no-id>",
a->aa_username ?: "<no-user>",
a->aa_rights & ACCESS_STREAMING ? 'S' : ' ',
a->aa_rights & ACCESS_FAILED_RECORDER ? 'F' : ' ',
a->aa_rights & ACCESS_ADMIN ? '*' : ' ',
a->aa_conn_limit,
+ a->aa_conn_limit_streaming,
+ a->aa_conn_limit_dvr,
a->aa_match ? ", matched" : "");
if (a->aa_profiles) {
tvhtrace("access", "%s", buf);
}
+/*
+ *
+ */
+static access_t *access_alloc(void)
+{
+ return calloc(1, sizeof(access_t));
+}
+
/*
*
*/
static void
access_update(access_t *a, access_entry_t *ae)
{
+ switch (ae->ae_conn_limit_type) {
+ case ACCESS_CONN_LIMIT_TYPE_ALL:
+ if (a->aa_conn_limit < ae->ae_conn_limit)
+ a->aa_conn_limit = ae->ae_conn_limit;
+ break;
+ case ACCESS_CONN_LIMIT_TYPE_STREAMING:
+ if (ae->ae_conn_limit && a->aa_conn_limit_streaming < ae->ae_conn_limit) {
+ a->aa_conn_limit_streaming = ae->ae_conn_limit;
+ a->aa_conn_limit = 0;
+ }
+ break;
+ case ACCESS_CONN_LIMIT_TYPE_DVR:
+ if (a->aa_conn_limit_dvr < ae->ae_conn_limit)
+ a->aa_conn_limit_dvr = ae->ae_conn_limit;
+ break;
+ }
+
if(a->aa_conn_limit < ae->ae_conn_limit)
a->aa_conn_limit = ae->ae_conn_limit;
access_t *
access_get(const char *username, const char *password, struct sockaddr *src)
{
- access_t *a = calloc(1, sizeof(*a));
+ access_t *a = access_alloc();
access_entry_t *ae;
int nouser = username == NULL || username[0] == '\0';
access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src)
{
- access_t *a = calloc(1, sizeof(*a));
+ access_t *a = access_alloc();
access_entry_t *ae;
int nouser = username == NULL || username[0] == '\0';
access_t *
access_get_by_username(const char *username)
{
- access_t *a = calloc(1, sizeof(*a));
+ access_t *a = access_alloc();
access_entry_t *ae;
a->aa_username = strdup(username);
access_t *
access_get_by_addr(struct sockaddr *src)
{
- access_t *a = calloc(1, sizeof(*a));
+ access_t *a = access_alloc();
access_entry_t *ae;
a->aa_representative = malloc(50);
return &ret;
}
+static htsmsg_t *
+access_entry_conn_limit_type_enum ( void *p )
+{
+ static struct strtab
+ conn_limit_type_tab[] = {
+ { "All (Streaming + DVR)", ACCESS_CONN_LIMIT_TYPE_ALL },
+ { "Streaming", ACCESS_CONN_LIMIT_TYPE_STREAMING },
+ { "DVR", ACCESS_CONN_LIMIT_TYPE_DVR },
+ };
+ return strtab2htsmsg(conn_limit_type_tab);
+}
+
const idclass_t access_entry_class = {
.ic_class = "access",
.ic_caption = "Access",
.name = "Admin",
.off = offsetof(access_entry_t, ae_admin),
},
+ {
+ .type = PT_INT,
+ .id = "conn_limit_type",
+ .name = "Connection Limit Type",
+ .off = offsetof(access_entry_t, ae_conn_limit_type),
+ .list = access_entry_conn_limit_type_enum,
+ },
{
.type = PT_U32,
.id = "conn_limit",
extern struct access_entry_queue access_entries;
+enum {
+ ACCESS_CONN_LIMIT_TYPE_ALL = 0,
+ ACCESS_CONN_LIMIT_TYPE_STREAMING,
+ ACCESS_CONN_LIMIT_TYPE_DVR,
+};
+
typedef struct access_entry {
idnode_t ae_id;
struct profile *ae_profile;
LIST_ENTRY(access_entry) ae_profile_link;
+ int ae_conn_limit_type;
uint32_t ae_conn_limit;
int ae_dvr;
htsmsg_t *aa_chtags;
int aa_match;
uint32_t aa_conn_limit;
+ uint32_t aa_conn_limit_streaming;
+ uint32_t aa_conn_limit_dvr;
+ uint32_t aa_conn_streaming;
+ uint32_t aa_conn_dvr;
} access_t;
TAILQ_HEAD(access_ticket_queue, access_ticket);
struct sockaddr sa;
access_t *aa;
uint32_t rec_count, net_count;
+ int c1, c2;
assert(de->de_s == NULL);
assert(de->de_chain == NULL);
return -EPERM;
}
- if (aa->aa_conn_limit) {
+ if (aa->aa_conn_limit || aa->aa_conn_limit_dvr) {
rec_count = dvr_usage_count(aa);
- net_count = tcp_connection_count(aa);
- if (rec_count + net_count >= aa->aa_conn_limit) {
+ net_count = aa->aa_conn_limit ? tcp_connection_count(aa) : 0;
+ /* the rule is: allow if one condition is OK */
+ c1 = aa->aa_conn_limit ? rec_count + net_count >= aa->aa_conn_limit : -1;
+ c2 = aa->aa_conn_limit_dvr ? rec_count >= aa->aa_conn_limit : -1;
+ if (c1 && c2) {
tvherror("dvr", "multiple connections are not allowed for user '%s' from '%s' "
- "(limit %u, active streaming %u, active DVR %u)",
+ "(limit %u, streaming %u, active DVR %u)",
aa->aa_username ?: "", aa->aa_representative ?: "",
aa->aa_conn_limit, rec_count, net_count);
return -EOVERFLOW;
tcp_server_launch_t *tsl, *res;
uint32_t used = 0, used2;
time_t started = dispatch_clock;
+ int c1, c2;
lock_assert(&global_lock);
LIST_FOREACH(tsl, &tcp_server_active, alink) {
if (tsl->fd == fd) {
res = tsl;
- if (!aa->aa_conn_limit)
+ if (!aa->aa_conn_limit && !aa->aa_conn_limit_streaming)
break;
continue;
}
if (res == NULL)
return NULL;
- if (aa->aa_conn_limit && used + (used2 = dvr_usage_count(aa)) >= aa->aa_conn_limit) {
- if (started + 3 < dispatch_clock) {
- tvherror("tcp", "multiple connections are not allowed for user '%s' from '%s' "
- "(limit %u, active streaming %u, active DVR %u)",
- aa->aa_username ?: "", aa->aa_representative ?: "", aa->aa_conn_limit,
- used, used2);
+ if (aa->aa_conn_limit || aa->aa_conn_limit_streaming) {
+ used2 = aa->aa_conn_limit ? dvr_usage_count(aa) : 0;
+ /* the rule is: allow if one condition is OK */
+ c1 = aa->aa_conn_limit ? used + used2 >= aa->aa_conn_limit : -1;
+ c2 = aa->aa_conn_limit_streaming ? used >= aa->aa_conn_limit_streaming : -1;
+
+ if (c1 && c2) {
+ if (started + 3 < dispatch_clock) {
+ tvherror("tcp", "multiple connections are not allowed for user '%s' from '%s' "
+ "(limit %u, active streaming %u, DVR %u)",
+ aa->aa_username ?: "", aa->aa_representative ?: "", aa->aa_conn_limit,
+ used, used2);
+ return NULL;
+ }
+ pthread_mutex_unlock(&global_lock);
+ usleep(250000);
+ pthread_mutex_lock(&global_lock);
+ if (tvheadend_running)
+ goto try_again;
return NULL;
}
- pthread_mutex_unlock(&global_lock);
- usleep(250000);
- pthread_mutex_lock(&global_lock);
- if (tvheadend_running)
- goto try_again;
- return NULL;
}
res->representative = aa->aa_representative ? strdup(aa->aa_representative) : NULL;
var list = 'enabled,username,password,prefix,' +
'webui,admin,' +
'streaming,adv_streaming,htsp_streaming,' +
- 'profile,conn_limit,dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
+ 'profile,conn_limit_type,conn_limit,' +
+ 'dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'dvr_config,channel_min,channel_max,channel_tag,comment';
var list2 = 'enabled,username,password,prefix,' +
'webui,admin,' +
'streaming,adv_streaming,htsp_streaming,' +
- 'profile,conn_limit,dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
+ 'profile,conn_limit_type,conn_limit,' +
+ 'dvr,htsp_dvr,all_dvr,all_rw_dvr,' +
'failed_dvr,dvr_config,channel_min,channel_max,channel_tag,' +
'comment';
all_rw_dvr: { width: 150 },
webui: { width: 140 },
admin: { width: 100 },
+ conn_limit_type:{ width: 160 },
conn_limit: { width: 160 },
channel_min: { width: 160 },
channel_max: { width: 160 }