MythTV  0.27pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends
hdhomerun_debug.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_debug.c
00003  *
00004  * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
00005  *
00006  * This library is free software; you can redistribute it and/or 
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 3 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
00018  * 
00019  * As a special exception to the GNU Lesser General Public License,
00020  * you may link, statically or dynamically, an application with a
00021  * publicly distributed version of the Library to produce an
00022  * executable file containing portions of the Library, and
00023  * distribute that executable file under terms of your choice,
00024  * without any of the additional requirements listed in clause 4 of
00025  * the GNU Lesser General Public License.
00026  * 
00027  * By "a publicly distributed version of the Library", we mean
00028  * either the unmodified Library as distributed by Silicondust, or a
00029  * modified version of the Library that is distributed under the
00030  * conditions defined in the GNU Lesser General Public License.
00031  */
00032 
00033 /*
00034  * The debug logging includes optional support for connecting to the
00035  * Silicondust support server. This option should not be used without
00036  * being explicitly enabled by the user. Debug information should be
00037  * limited to information useful to diagnosing a problem.
00038  *  - Silicondust.
00039  */
00040 
00041 #include "hdhomerun.h"
00042 
00043 #if !defined(HDHOMERUN_DEBUG_HOST)
00044 #define HDHOMERUN_DEBUG_HOST "debug.silicondust.com"
00045 #endif
00046 #if !defined(HDHOMERUN_DEBUG_PORT)
00047 #define HDHOMERUN_DEBUG_PORT 8002
00048 #endif
00049 
00050 #define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000
00051 #define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000
00052 #define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000
00053 
00054 struct hdhomerun_debug_message_t
00055 {
00056         struct hdhomerun_debug_message_t *next;
00057         struct hdhomerun_debug_message_t *prev;
00058         char buffer[2048];
00059 };
00060 
00061 struct hdhomerun_debug_t
00062 {
00063         pthread_t thread;
00064         volatile bool_t enabled;
00065         volatile bool_t terminate;
00066         char *prefix;
00067 
00068         pthread_mutex_t print_lock;
00069         pthread_mutex_t queue_lock;
00070         pthread_mutex_t send_lock;
00071 
00072         struct hdhomerun_debug_message_t *queue_head;
00073         struct hdhomerun_debug_message_t *queue_tail;
00074         uint32_t queue_depth;
00075 
00076         uint64_t connect_delay;
00077 
00078         char *file_name;
00079         FILE *file_fp;
00080         hdhomerun_sock_t sock;
00081 };
00082 
00083 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
00084 
00085 struct hdhomerun_debug_t *hdhomerun_debug_create(void)
00086 {
00087         struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)calloc(1, sizeof(struct hdhomerun_debug_t));
00088         if (!dbg) {
00089                 return NULL;
00090         }
00091 
00092         dbg->sock = HDHOMERUN_SOCK_INVALID;
00093 
00094         pthread_mutex_init(&dbg->print_lock, NULL);
00095         pthread_mutex_init(&dbg->queue_lock, NULL);
00096         pthread_mutex_init(&dbg->send_lock, NULL);
00097 
00098         if (pthread_create(&dbg->thread, NULL, &hdhomerun_debug_thread_execute, dbg) != 0) {
00099                 free(dbg);
00100                 return NULL;
00101         }
00102 
00103         return dbg;
00104 }
00105 
00106 void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
00107 {
00108         if (!dbg) {
00109                 return;
00110         }
00111 
00112         dbg->terminate = TRUE;
00113         pthread_join(dbg->thread, NULL);
00114 
00115         if (dbg->prefix) {
00116                 free(dbg->prefix);
00117         }
00118         if (dbg->file_name) {
00119                 free(dbg->file_name);
00120         }
00121         if (dbg->file_fp) {
00122                 fclose(dbg->file_fp);
00123         }
00124         if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
00125                 hdhomerun_sock_destroy(dbg->sock);
00126         }
00127 
00128         free(dbg);
00129 }
00130 
00131 /* Send lock held by caller */
00132 static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg)
00133 {
00134         if (dbg->file_fp) {
00135                 fclose(dbg->file_fp);
00136                 dbg->file_fp = NULL;
00137         }
00138 
00139         if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
00140                 hdhomerun_sock_destroy(dbg->sock);
00141                 dbg->sock = HDHOMERUN_SOCK_INVALID;
00142         }
00143 }
00144 
00145 void hdhomerun_debug_close(struct hdhomerun_debug_t *dbg, uint64_t timeout)
00146 {
00147         if (!dbg) {
00148                 return;
00149         }
00150 
00151         if (timeout > 0) {
00152                 hdhomerun_debug_flush(dbg, timeout);
00153         }
00154 
00155         pthread_mutex_lock(&dbg->send_lock);
00156         hdhomerun_debug_close_internal(dbg);
00157         dbg->connect_delay = 0;
00158         pthread_mutex_unlock(&dbg->send_lock);
00159 }
00160 
00161 void hdhomerun_debug_set_filename(struct hdhomerun_debug_t *dbg, const char *filename)
00162 {
00163         if (!dbg) {
00164                 return;
00165         }
00166 
00167         pthread_mutex_lock(&dbg->send_lock);
00168 
00169         if (!filename && !dbg->file_name) {
00170                 pthread_mutex_unlock(&dbg->send_lock);
00171                 return;
00172         }
00173         if (filename && dbg->file_name) {
00174                 if (strcmp(filename, dbg->file_name) == 0) {
00175                         pthread_mutex_unlock(&dbg->send_lock);
00176                         return;
00177                 }
00178         }
00179 
00180         hdhomerun_debug_close_internal(dbg);
00181         dbg->connect_delay = 0;
00182 
00183         if (dbg->file_name) {
00184                 free(dbg->file_name);
00185                 dbg->file_name = NULL;
00186         }
00187         if (filename) {
00188                 dbg->file_name = strdup(filename);
00189         }
00190 
00191         pthread_mutex_unlock(&dbg->send_lock);
00192 }
00193 
00194 void hdhomerun_debug_set_prefix(struct hdhomerun_debug_t *dbg, const char *prefix)
00195 {
00196         if (!dbg) {
00197                 return;
00198         }
00199 
00200         pthread_mutex_lock(&dbg->print_lock);
00201 
00202         if (dbg->prefix) {
00203                 free(dbg->prefix);
00204                 dbg->prefix = NULL;
00205         }
00206 
00207         if (prefix) {
00208                 dbg->prefix = strdup(prefix);
00209         }
00210 
00211         pthread_mutex_unlock(&dbg->print_lock);
00212 }
00213 
00214 void hdhomerun_debug_enable(struct hdhomerun_debug_t *dbg)
00215 {
00216         if (!dbg) {
00217                 return;
00218         }
00219 
00220         dbg->enabled = TRUE;
00221 }
00222 
00223 void hdhomerun_debug_disable(struct hdhomerun_debug_t *dbg)
00224 {
00225         if (!dbg) {
00226                 return;
00227         }
00228 
00229         dbg->enabled = FALSE;
00230 }
00231 
00232 bool_t hdhomerun_debug_enabled(struct hdhomerun_debug_t *dbg)
00233 {
00234         if (!dbg) {
00235                 return FALSE;
00236         }
00237 
00238         return dbg->enabled;
00239 }
00240 
00241 void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout)
00242 {
00243         if (!dbg) {
00244                 return;
00245         }
00246 
00247         timeout = getcurrenttime() + timeout;
00248 
00249         while (getcurrenttime() < timeout) {
00250                 pthread_mutex_lock(&dbg->queue_lock);
00251                 struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00252                 pthread_mutex_unlock(&dbg->queue_lock);
00253 
00254                 if (!message) {
00255                         return;
00256                 }
00257 
00258                 msleep_approx(10);
00259         }
00260 }
00261 
00262 void hdhomerun_debug_printf(struct hdhomerun_debug_t *dbg, const char *fmt, ...)
00263 {
00264         va_list args;
00265         va_start(args, fmt);
00266         hdhomerun_debug_vprintf(dbg, fmt, args);
00267         va_end(args);
00268 }
00269 
00270 void hdhomerun_debug_vprintf(struct hdhomerun_debug_t *dbg, const char *fmt, va_list args)
00271 {
00272         if (!dbg) {
00273                 return;
00274         }
00275         if (!dbg->enabled) {
00276                 return;
00277         }
00278 
00279         struct hdhomerun_debug_message_t *message = (struct hdhomerun_debug_message_t *)malloc(sizeof(struct hdhomerun_debug_message_t));
00280         if (!message) {
00281                 return;
00282         }
00283 
00284         char *ptr = message->buffer;
00285         char *end = message->buffer + sizeof(message->buffer) - 2;
00286         *end = 0;
00287 
00288         /*
00289          * Timestamp.
00290          */
00291         time_t current_time = time(NULL);
00292         ptr += strftime(ptr, end - ptr, "%Y%m%d-%H:%M:%S ", localtime(&current_time));
00293         if (ptr > end) {
00294                 ptr = end;
00295         }
00296 
00297         /*
00298          * Debug prefix.
00299          */
00300         pthread_mutex_lock(&dbg->print_lock);
00301 
00302         if (dbg->prefix) {
00303                 int len = snprintf(ptr, end - ptr, "%s ", dbg->prefix);
00304                 len = (len <= 0) ? 0 : len;
00305                 ptr += len;
00306                 if (ptr > end) {
00307                         ptr = end;
00308                 }
00309         }
00310 
00311         pthread_mutex_unlock(&dbg->print_lock);
00312 
00313         /*
00314          * Message text.
00315          */
00316         int len = vsnprintf(ptr, end - ptr, fmt, args);
00317         len = (len < 0) ? 0 : len; /* len does not include null */
00318         ptr += len;
00319         if (ptr > end) {
00320                 ptr = end;
00321         }
00322 
00323         /*
00324          * Force newline.
00325          */
00326         if ((ptr[-1] != '\n') && (ptr + 1 <= end)) {
00327                 *ptr++ = '\n';
00328         }
00329 
00330         /*
00331          * Force NULL.
00332          */
00333         if (ptr + 1 > end) {
00334                 ptr = end - 1;
00335         }
00336         *ptr++ = 0;
00337 
00338         /*
00339          * Enqueue.
00340          */
00341         pthread_mutex_lock(&dbg->queue_lock);
00342 
00343         message->prev = NULL;
00344         message->next = dbg->queue_head;
00345         dbg->queue_head = message;
00346         if (message->next) {
00347                 message->next->prev = message;
00348         } else {
00349                 dbg->queue_tail = message;
00350         }
00351         dbg->queue_depth++;
00352 
00353         pthread_mutex_unlock(&dbg->queue_lock);
00354 }
00355 
00356 /* Send lock held by caller */
00357 static bool_t hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00358 {
00359         if (!dbg->file_fp) {
00360                 uint64_t current_time = getcurrenttime();
00361                 if (current_time < dbg->connect_delay) {
00362                         return FALSE;
00363                 }
00364                 dbg->connect_delay = current_time + 30*1000;
00365 
00366                 dbg->file_fp = fopen(dbg->file_name, "a");
00367                 if (!dbg->file_fp) {
00368                         return FALSE;
00369                 }
00370         }
00371 
00372         fprintf(dbg->file_fp, "%s", message->buffer);
00373         fflush(dbg->file_fp);
00374 
00375         return TRUE;
00376 }
00377 
00378 /* Send lock held by caller */
00379 static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00380 {
00381         if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
00382                 uint64_t current_time = getcurrenttime();
00383                 if (current_time < dbg->connect_delay) {
00384                         return FALSE;
00385                 }
00386                 dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME;
00387 
00388                 dbg->sock = hdhomerun_sock_create_tcp();
00389                 if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
00390                         return FALSE;
00391                 }
00392 
00393                 uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST);
00394                 if (remote_addr == 0) {
00395                         hdhomerun_debug_close_internal(dbg);
00396                         return FALSE;
00397                 }
00398 
00399                 if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) {
00400                         hdhomerun_debug_close_internal(dbg);
00401                         return FALSE;
00402                 }
00403         }
00404 
00405         size_t length = strlen(message->buffer);
00406         if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) {
00407                 hdhomerun_debug_close_internal(dbg);
00408                 return FALSE;
00409         }
00410 
00411         return TRUE;
00412 }
00413 
00414 static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
00415 {
00416         pthread_mutex_lock(&dbg->send_lock);
00417 
00418         bool_t ret;
00419         if (dbg->file_name) {
00420                 ret = hdhomerun_debug_output_message_file(dbg, message);
00421         } else {
00422                 ret = hdhomerun_debug_output_message_sock(dbg, message);
00423         }
00424 
00425         pthread_mutex_unlock(&dbg->send_lock);
00426         return ret;
00427 }
00428 
00429 static void hdhomerun_debug_pop_and_free_message(struct hdhomerun_debug_t *dbg)
00430 {
00431         pthread_mutex_lock(&dbg->queue_lock);
00432 
00433         struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00434         dbg->queue_tail = message->prev;
00435         if (message->prev) {
00436                 message->prev->next = NULL;
00437         } else {
00438                 dbg->queue_head = NULL;
00439         }
00440         dbg->queue_depth--;
00441 
00442         pthread_mutex_unlock(&dbg->queue_lock);
00443 
00444         free(message);
00445 }
00446 
00447 static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
00448 {
00449         struct hdhomerun_debug_t *dbg = (struct hdhomerun_debug_t *)arg;
00450 
00451         while (!dbg->terminate) {
00452 
00453                 pthread_mutex_lock(&dbg->queue_lock);
00454                 struct hdhomerun_debug_message_t *message = dbg->queue_tail;
00455                 uint32_t queue_depth = dbg->queue_depth;
00456                 pthread_mutex_unlock(&dbg->queue_lock);
00457 
00458                 if (!message) {
00459                         msleep_approx(250);
00460                         continue;
00461                 }
00462 
00463                 if (queue_depth > 1024) {
00464                         hdhomerun_debug_pop_and_free_message(dbg);
00465                         continue;
00466                 }
00467 
00468                 if (!hdhomerun_debug_output_message(dbg, message)) {
00469                         msleep_approx(250);
00470                         continue;
00471                 }
00472 
00473                 hdhomerun_debug_pop_and_free_message(dbg);
00474         }
00475 
00476         return 0;
00477 }