From: Jaroslav Kysela Date: Thu, 4 Jun 2015 20:08:19 +0000 (+0200) Subject: ACL: Add connection limit type X-Git-Tag: v4.2.1~2368 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cbfa37361011d5f5fa0a4368b63f686aba58292;p=thirdparty%2Ftvheadend.git ACL: Add connection limit type --- diff --git a/docs/html/config_access.html b/docs/html/config_access.html index 2b163bae7..dfde67b71 100644 --- a/docs/html/config_access.html +++ b/docs/html/config_access.html @@ -78,6 +78,10 @@ The columns have the following functions:
Specify a streaming profile to be used when this user logs in; use the (default) stream if not specified. +
Connection Limit Type +
+ Allow to restrict the limit for connections to streaming or DVR only. +
Limit Connections
If set, this will limit the number of concurrent streaming connections and diff --git a/src/access.c b/src/access.c index 1e57ca2b0..21c8ec74f 100644 --- a/src/access.c +++ b/src/access.c @@ -369,7 +369,7 @@ access_dump_a(access_t *a) 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 ?: "", a->aa_username ?: "", a->aa_rights & ACCESS_STREAMING ? 'S' : ' ', @@ -383,6 +383,8 @@ access_dump_a(access_t *a) 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) { @@ -443,12 +445,37 @@ access_dump_a(access_t *a) 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; @@ -488,7 +515,7 @@ access_update(access_t *a, access_entry_t *ae) 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'; @@ -556,7 +583,7 @@ access_t * 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'; @@ -622,7 +649,7 @@ access_get_hashed(const char *username, const uint8_t digest[20], 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); @@ -656,7 +683,7 @@ access_get_by_username(const char *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); @@ -1201,6 +1228,18 @@ access_entry_profile_get(void *o) 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", @@ -1315,6 +1354,13 @@ const idclass_t access_entry_class = { .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", diff --git a/src/access.h b/src/access.h index 411febacd..952428fbf 100644 --- a/src/access.h +++ b/src/access.h @@ -63,6 +63,12 @@ TAILQ_HEAD(access_entry_queue, access_entry); 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; @@ -80,6 +86,7 @@ typedef struct access_entry { struct profile *ae_profile; LIST_ENTRY(access_entry) ae_profile_link; + int ae_conn_limit_type; uint32_t ae_conn_limit; int ae_dvr; @@ -117,6 +124,10 @@ typedef struct access { 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); diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c index 48dfd8119..ee3d721d3 100644 --- a/src/dvr/dvr_rec.c +++ b/src/dvr/dvr_rec.c @@ -70,6 +70,7 @@ dvr_rec_subscribe(dvr_entry_t *de) 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); @@ -92,12 +93,15 @@ dvr_rec_subscribe(dvr_entry_t *de) 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; diff --git a/src/tcp.c b/src/tcp.c index f35de01eb..7c5ce6311 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -461,6 +461,7 @@ tcp_connection_launch tcp_server_launch_t *tsl, *res; uint32_t used = 0, used2; time_t started = dispatch_clock; + int c1, c2; lock_assert(&global_lock); @@ -474,7 +475,7 @@ try_again: 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; } @@ -484,20 +485,27 @@ try_again: 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; diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js index c8759461a..c3951aa2a 100644 --- a/src/webui/static/app/acleditor.js +++ b/src/webui/static/app/acleditor.js @@ -7,13 +7,15 @@ tvheadend.acleditor = function(panel, index) 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'; @@ -36,6 +38,7 @@ tvheadend.acleditor = function(panel, index) 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 }