static int delete_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd) { long c = 0; char *buf; unsigned int len = ((2 * CMYTH_LONGLONG_LEN) + (6 * CMYTH_TIMESTAMP_LEN) + (14 * CMYTH_LONG_LEN)); char start_ts[CMYTH_TIMESTAMP_LEN + 1]; char end_ts[CMYTH_TIMESTAMP_LEN + 1]; char rec_start_ts[CMYTH_TIMESTAMP_LEN + 1]; char rec_end_ts[CMYTH_TIMESTAMP_LEN + 1]; char originalairdate[CMYTH_TIMESTAMP_LEN + 1]; char lastmodified[CMYTH_TIMESTAMP_LEN + 1]; int err; int count; long r; int ret; if (!prog) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program info\n", __FUNCTION__); return -EINVAL; } #define S(a) ((a) == NULL ? "" : (a)) len += strlen(S(prog->proginfo_title)); len += strlen(S(prog->proginfo_subtitle)); len += strlen(S(prog->proginfo_description)); len += strlen(S(prog->proginfo_category)); len += strlen(S(prog->proginfo_chanstr)); len += strlen(S(prog->proginfo_chansign)); len += strlen(S(prog->proginfo_channame)); len += strlen(S(prog->proginfo_url)); len += strlen(S(prog->proginfo_hostname)); len += strlen(S(prog->proginfo_playgroup)); len += strlen(S(prog->proginfo_recpriority_2)); len += strlen(S(prog->proginfo_storagegroup)); buf = alloca(len + 1+2048); if (!buf) { return -ENOMEM; } if(control->conn_version < 14) { cmyth_timestamp_to_string(start_ts, prog->proginfo_start_ts); cmyth_timestamp_to_string(end_ts, prog->proginfo_end_ts); cmyth_timestamp_to_string(rec_start_ts, prog->proginfo_rec_start_ts); cmyth_timestamp_to_string(rec_end_ts, prog->proginfo_rec_end_ts); cmyth_timestamp_to_string(originalairdate, prog->proginfo_originalairdate); cmyth_timestamp_to_string(lastmodified, prog->proginfo_lastmodified); } else { cmyth_datetime_to_string(start_ts, prog->proginfo_start_ts); cmyth_datetime_to_string(end_ts, prog->proginfo_end_ts); cmyth_datetime_to_string(rec_start_ts, prog->proginfo_rec_start_ts); cmyth_datetime_to_string(rec_end_ts, prog->proginfo_rec_end_ts); cmyth_datetime_to_string(originalairdate, prog->proginfo_originalairdate); cmyth_datetime_to_string(lastmodified, prog->proginfo_lastmodified); } if(control->conn_version > 32) { cmyth_timestamp_to_isostring(originalairdate, prog->proginfo_originalairdate); } if(control->conn_version < 12) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: delete not supported with protocol ver %d\n", __FUNCTION__, control->conn_version); return -EINVAL; } else { sprintf(buf, "%s 0[]:[]" "%s[]:[]%s[]:[]%s[]:[]%s[]:[]%ld[]:[]" "%s[]:[]%s[]:[]%s[]:[]%s[]:[]%ld[]:[]" "%ld[]:[]%s[]:[]%s[]:[]%s[]:[]%ld[]:[]" "%ld[]:[]%s[]:[]%ld[]:[]%ld[]:[]%ld[]:[]" "%s[]:[]%ld[]:[]%ld[]:[]%ld[]:[]%ld[]:[]" "%ld[]:[]%s[]:[]%s[]:[]%ld[]:[]%ld[]:[]" "%s[]:[]%s[]:[]%s[]:[]%s[]:[]" "%s[]:[]%s[]:[]%s[]:[]%s[]:[]", cmd, S(prog->proginfo_title), S(prog->proginfo_subtitle), S(prog->proginfo_description), S(prog->proginfo_category), prog->proginfo_chanId, S(prog->proginfo_chanstr), S(prog->proginfo_chansign), S(prog->proginfo_chanicon), S(prog->proginfo_url), (unsigned long)(prog->proginfo_Length >> 32), (unsigned long)(prog->proginfo_Length & 0xffffffff), start_ts, end_ts, S(prog->proginfo_unknown_0), prog->proginfo_recording, prog->proginfo_override, S(prog->proginfo_hostname), prog->proginfo_source_id, prog->proginfo_card_id, prog->proginfo_input_id, S(prog->proginfo_rec_priority), prog->proginfo_rec_status, prog->proginfo_record_id, prog->proginfo_rec_type, prog->proginfo_rec_dups, prog->proginfo_unknown_1, rec_start_ts, rec_end_ts, prog->proginfo_repeat, prog->proginfo_program_flags, S(prog->proginfo_recgroup), S(prog->proginfo_chancommfree), S(prog->proginfo_chan_output_filters), S(prog->proginfo_seriesid), S(prog->proginfo_programid), lastmodified, S(prog->proginfo_stars), originalairdate); if (control->conn_version >= 15) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_hasairdate); } if (control->conn_version >= 18) { sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_playgroup)); } if (control->conn_version >= 25) { sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_recpriority_2)); } if (control->conn_version >= 31) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_parentid); } if (control->conn_version >= 32) { sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_storagegroup)); } if (control->conn_version >= 35) { sprintf(buf + strlen(buf), "%ld[]:[]%ld[]:[]%ld[]:[]", prog->proginfo_audioproperties, prog->proginfo_videoproperties, prog->proginfo_subtitletype); } if (control->conn_version >= 41) { sprintf(buf + strlen(buf), "%s[]:[]", S(prog->proginfo_prodyear)); } } #undef S pthread_mutex_lock(&mutex); if ((err = cmyth_send_message(control, buf)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(control); if ((r=cmyth_rcv_long(control, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } /* * XXX: for some reason, this seems to return an error, even though * it succeeds... */ ret = 0; out: pthread_mutex_unlock(&mutex); return ret; }
int cmyth_conn_get_freespace(cmyth_conn_t control, long long *total, long long *used) { int err, count, ret = 0; int r; char msg[256]; char reply[256]; long long lreply; if (control == NULL) return -EINVAL; if ((total == NULL) || (used == NULL)) return -EINVAL; pthread_mutex_lock(&mutex); if (control->conn_version >= 32) { snprintf(msg, sizeof(msg), "QUERY_FREE_SPACE_SUMMARY"); } else if (control->conn_version >= 17) { snprintf(msg, sizeof(msg), "QUERY_FREE_SPACE"); } else { snprintf(msg, sizeof(msg), "QUERY_FREESPACE"); } if ((err = cmyth_send_message(control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } if ((count=cmyth_rcv_length(control)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } if (control->conn_version >= 17) { if ((r=cmyth_rcv_long_long(control, &err, &lreply, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_long_long() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } *total = lreply; if ((r=cmyth_rcv_long_long(control, &err, &lreply, count-r)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_long_long() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } *used = lreply; } else { if ((r=cmyth_rcv_string(control, &err, reply, sizeof(reply)-1, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } *total = atoi(reply); if ((r=cmyth_rcv_string(control, &err, reply, sizeof(reply)-1, count-r)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } *used = atoi(reply); *used *= 1024; *total *= 1024; } out: pthread_mutex_unlock(&mutex); return ret; }
static cmyth_conn_t cmyth_conn_connect(char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf, int event) { cmyth_conn_t conn; char announcement[256]; unsigned long tmp_ver; int attempt = 0; top: conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return NULL; } /* * Find out what the Myth Protocol Version is for this connection. * Loop around until we get agreement from the server. */ if (attempt == 0) tmp_ver = conn->conn_version; conn->conn_version = tmp_ver; if (tmp_ver >= 62) { myth_protomap_t *map = protomap; while (map->version != 0 && map->version != tmp_ver) map++; if (map->version == 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } sprintf(announcement, "MYTH_PROTO_VERSION %ld %04X", conn->conn_version, map->token); } else { sprintf(announcement, "MYTH_PROTO_VERSION %ld", conn->conn_version); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_version(conn, &tmp_ver) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_version() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_ERROR, "%s: asked for version %ld, got version %ld\n", __FUNCTION__, conn->conn_version, tmp_ver); if (conn->conn_version != tmp_ver) { if (attempt == 1) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } attempt = 1; ref_release(conn); goto top; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: agreed on Version %ld protocol\n", __FUNCTION__, conn->conn_version); sprintf(announcement, "ANN Playback %s %d", my_hostname, event); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } return conn; shut: ref_release(conn); return NULL; }
static int cmyth_storagegroup_update_fileinfo(cmyth_conn_t control, cmyth_storagegroup_file_t file) { char msg[256]; int count; int err = 0; int consumed; /* = profiles;*/ char tmp_str[2048]; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } if (!file) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no file specified\n", __FUNCTION__); return -EINVAL; } snprintf(msg, sizeof(msg), "QUERY_SG_FILEQUERY[]:[]%s[]:[]%s[]:[]%s", file->hostname , file->storagegroup, file->filename); err = cmyth_send_message(control, msg); if (err < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); return err; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); return -1; } consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); return -1; } else if (count == 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: QUERY_SG_FILEQUERY failed(%s)\n", __FUNCTION__, tmp_str); return -1; } consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str), count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); return -1; } file->lastmodified = atol(tmp_str); consumed = cmyth_rcv_new_int64(control, &err, &(file->size), count, 1); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_int64() failed (%d)\n", __FUNCTION__, count); return -1; } return 0; }
/* Sergio: Added to support the new livetv protocol */ int cmyth_recorder_spawn_chain_livetv(cmyth_recorder_t rec) { int err; int ret = -1; char msg[256]; char myhostname[32]; char datestr[32]; time_t t; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } pthread_mutex_lock(&mutex); /* Get our own IP address */ gethostname(myhostname, 32); /* Get the current date and time to create a unique id */ t = time(NULL); strftime(datestr, 32, "%Y-%m-%dT%H:%M:%S", localtime(&t)); /* Now build the SPAWN_LIVETV message */ if (rec->rec_conn->conn_version >=50) { snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SPAWN_LIVETV[]:[]live-%s-%s[]:[]%d[]:[]", rec->rec_id, myhostname, datestr, 0); } else { snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]SPAWN_LIVETV[]:[]live-%s-%s[]:[]%d", rec->rec_id, myhostname, datestr, 0); } if ((err=cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } if ((err=cmyth_rcv_okay(rec->rec_conn, "ok")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed (%d)\n", __FUNCTION__, err); goto fail; } /* Create an empty livetv chain with the ID used in the spawn command */ snprintf(msg, sizeof(msg), "live-%s-%s[]:[]", myhostname, datestr); rec->rec_livetv_chain = cmyth_livetv_chain_create(msg); ret = 0; fail: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_conn_connect_ring(char *server, unsigned short port, unsigned buflen * cmyth_recorder_t rec) * * Scope: PUBLIC * * Description: * * Create a new ring buffer connection for use transferring live-tv * using the MythTV protocol. Return a pointer to the newly created * ring buffer connection. The ring buffer connection is returned * held, and may be released using ref_release(). * * Return Value: * * Success: Non-NULL cmyth_conn_t (this is a pointer type) * * Failure: NULL cmyth_conn_t */ int cmyth_conn_connect_ring(cmyth_recorder_t rec, unsigned buflen, int tcp_rcvbuf) { cmyth_conn_t conn; char *announcement; int ann_size = sizeof("ANN RingBuffer "); char *server; unsigned short port; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: rec is NULL\n", __FUNCTION__); return -1; } server = rec->rec_server; port = rec->rec_port; cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting ringbuffer\n", __FUNCTION__); conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting ringbuffer, conn = %p\n", __FUNCTION__, conn); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return -1; } ann_size += CMYTH_LONG_LEN + strlen(my_hostname); announcement = malloc(ann_size); if (!announcement) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc(%d) failed for announcement\n", __FUNCTION__, ann_size); goto shut; } sprintf(announcement, "ANN RingBuffer %s %d", my_hostname, rec->rec_id); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); free(announcement); goto shut; } free(announcement); if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } rec->rec_ring->conn_data = conn; return 0; shut: ref_release(conn); return -1; }
cmyth_storagegroup_filelist_t cmyth_storagegroup_get_filelist(cmyth_conn_t control,char *storagegroup, char *hostname) { char msg[256]; int res = 0; int count = 0; int err = 0; int i = 0; int listsize = 10; cmyth_storagegroup_filelist_t ret = NULL; cmyth_storagegroup_file_t file = NULL; int consumed = 0; /* = profiles; */ char tmp_str[32768]; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } pthread_mutex_lock(&control->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_SG_GETFILELIST[]:[]%s[]:[]%s[]:[][]:[]1", hostname, storagegroup); err = cmyth_send_message(control, msg); if (err < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto out; } ret = cmyth_storagegroup_filelist_create(); if (!ret) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for list\n", __FUNCTION__); goto out; } ret->storagegroup_filelist_count = 0; ret->storagegroup_filelist_list = malloc(listsize * sizeof(cmyth_storagegroup_file_t)); if (!ret->storagegroup_filelist_list) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for filelist list\n", __FUNCTION__); ref_release(ret); ret = NULL; goto out; } while (count) { consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); ref_release(ret); ret = NULL; goto out; } if (res>listsize-1) { listsize += 10; ret->storagegroup_filelist_list = realloc(ret->storagegroup_filelist_list, listsize * sizeof(cmyth_storagegroup_file_t)); if (!ret->storagegroup_filelist_list) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: realloc() failed for filelist list\n", __FUNCTION__); ref_release(ret); ret = NULL; goto out; } } file = cmyth_storagegroup_file_create(); file->filename = ref_strdup(tmp_str); file->storagegroup = ref_strdup(storagegroup); file->hostname = ref_strdup(hostname); file->size = 0; file->lastmodified = 0; ret->storagegroup_filelist_list[res] = file; res++; } ret->storagegroup_filelist_count = res; for(i = 0;i < ret->storagegroup_filelist_count;i++) { cmyth_storagegroup_update_fileinfo(control, ret->storagegroup_filelist_list[i]); } cmyth_dbg(CMYTH_DBG_DEBUG, "%s: results= %d\n", __FUNCTION__, res); out: pthread_mutex_unlock(&control->conn_mutex); return ret; }
cmyth_proginfo_t cmyth_proginfo_get_from_timeslot(cmyth_conn_t control, uint32_t chanid, const cmyth_timestamp_t recstartts) { int err = 0; int count, i; char msg[4096]; cmyth_proginfo_t prog = NULL; cmyth_proglist_t list = NULL; char time[15]; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } if ((err = cmyth_timestamp_to_numstring(time, recstartts)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_timestamp_to_numstring() failed (%d)\n", __FUNCTION__, err); return NULL; } if(control->conn_version >= 32) { pthread_mutex_lock(&control->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDING TIMESLOT %"PRIu32" %s", chanid, time); if ((err=cmyth_send_message(control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto out; } i = cmyth_rcv_string(control, &err, msg, sizeof(msg), count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed\n", __FUNCTION__); goto out; } count -= i; if (strcmp(msg, "OK") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: didn't recieve OK as response\n", __FUNCTION__); goto out; } prog = cmyth_proginfo_create(); if (cmyth_rcv_proginfo(control, &err, prog, count) != count) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_proginfo() < count\n", __FUNCTION__); goto out; } pthread_mutex_unlock(&control->conn_mutex); return prog; out: pthread_mutex_unlock(&control->conn_mutex); if(prog) ref_release(prog); return NULL; } else { list = cmyth_proglist_get_all_recorded(control); if (!list) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program list\n", __FUNCTION__); } count = cmyth_proglist_get_count(list); for (i = 0;i < count; i++) { prog = cmyth_proglist_get_item(list, i); if (!prog) { cmyth_dbg(CMYTH_DBG_DEBUG, "%s: no program info\n", __FUNCTION__); continue; } if (cmyth_timestamp_compare(prog->proginfo_rec_start_ts, recstartts) != 0 || prog->proginfo_chanId != chanid) { ref_release(prog); prog = NULL; continue; } break; } ref_release(list); return prog; } }
/* * cmyth_proginfo_stop_recording() * * Scope: PUBLIC * * Description * * Make a request on the control connection 'control' to ask the * MythTV back end to stop recording the program described in 'prog'. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_proginfo_stop_recording(cmyth_conn_t control, cmyth_proginfo_t prog) { int32_t c = 0; int err = 0; int count = 0; int r = 0; int ret = 0; char *buf; char *proginfo; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } if (control->conn_version < 12) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: stop not supported with protocol ver %d\n", __FUNCTION__, control->conn_version); return -EINVAL; } proginfo = cmyth_proginfo_string(control, prog); if (proginfo == NULL) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: program_info failed.\n", __FUNCTION__); return -EINVAL; } buf = malloc(strlen(proginfo) + 21 + 1); if (!buf) { free(proginfo); return -ENOMEM; } sprintf(buf, "STOP_RECORDING 0[]:[]%s", proginfo); free(proginfo); pthread_mutex_lock(&control->conn_mutex); if ((err = cmyth_send_message(control, buf)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(control); if ((r = cmyth_rcv_int32(control, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } out: pthread_mutex_unlock(&control->conn_mutex); free(buf); return ret; }
/* * cmyth_proginfo_fill() * * Scope: PRIVATE (static) * * Description * * Fill out a (possibly incomplete) program info. Incomplete program * info comes from program listings. Since this modifies the contents of * the supplied program info, it must never be called with a program info * that has more than one reference). * * Return Value: * * Success: 0 * * Failure: a negative error code. */ static int cmyth_proginfo_fill(cmyth_conn_t control, cmyth_proginfo_t prog) { int err = 0; int count = 0; int ret = 0; char *buf; char *proginfo; int64_t length = 0; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } if (!prog) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program info\n", __FUNCTION__); return -EINVAL; } proginfo = cmyth_proginfo_string(control, prog); if (proginfo == NULL) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: program_info failed.\n", __FUNCTION__); return -EINVAL; } buf = malloc(strlen(proginfo) + 34 + 1); if (!buf) { free(proginfo); return -ENOMEM; } sprintf(buf, "FILL_PROGRAM_INFO cmyth[]:[]0[]:[]%s", proginfo); free(proginfo); pthread_mutex_lock(&control->conn_mutex); length = prog->proginfo_Length; if ((err = cmyth_send_message(control, buf)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } if (cmyth_rcv_proginfo(control, &err, prog, count) != count) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_proginfo() < count\n", __FUNCTION__); ret = err; goto out; } /* * Myth seems to cache the program length, rather than call stat() * every time it needs to know. Using FILL_PROGRAM_INFO has worked * to force mythbackend to call stat() and return the correct length. * * However, some users are reporting that FILL_PROGRAM_INFO is * returning 0 for the program length. In that case, the original * number is still probably wrong, but it's better than 0. */ if (prog->proginfo_Length == 0) { prog->proginfo_Length = length; } out: pthread_mutex_unlock(&control->conn_mutex); free(buf); return ret; }
cmyth_proginfo_t cmyth_proginfo_get_from_basename(cmyth_conn_t control, const char* basename) { int err = 0; int count, i; char msg[4096]; char *base; cmyth_proginfo_t prog = NULL; cmyth_proglist_t list = NULL; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } /* * mythbackend doesn't support spaces in basenames * when doing QUERY_RECORDING. If there are spaces, fallback * to enumerating all recordings */ if(control->conn_version >= 32 && strchr(basename, ' ') == NULL) { pthread_mutex_lock(&control->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDING BASENAME %s", basename); if ((err=cmyth_send_message(control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto out; } i = cmyth_rcv_string(control, &err, msg, sizeof(msg), count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed\n", __FUNCTION__); goto out; } count -= i; if (strcmp(msg, "OK") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: didn't recieve OK as response\n", __FUNCTION__); goto out; } prog = cmyth_proginfo_create(); if (cmyth_rcv_proginfo(control, &err, prog, count) != count) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_proginfo() < count\n", __FUNCTION__); goto out; } pthread_mutex_unlock(&control->conn_mutex); return prog; out: pthread_mutex_unlock(&control->conn_mutex); if(prog) ref_release(prog); return NULL; } else { list = cmyth_proglist_get_all_recorded(control); if (!list) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program list\n", __FUNCTION__); } count = cmyth_proglist_get_count(list); for (i = 0;i < count; i++) { prog = cmyth_proglist_get_item(list, i); if (!prog) { cmyth_dbg(CMYTH_DBG_DEBUG, "%s: no program info\n", __FUNCTION__); continue; } base = strrchr(prog->proginfo_pathname, '/'); if (!base || strcmp(base+1, basename) !=0) { ref_release(prog); prog = NULL; continue; } break; } ref_release(list); return prog; } }
/* * cmyth_ringbuf_seek( * cmyth_ringbuf_t file, long long offset, int whence) * * Scope: PUBLIC * * Description * * Seek to a new position in the file based on the value of whence: * SEEK_SET * The offset is set to offset bytes. * SEEK_CUR * The offset is set to the current position plus offset bytes. * SEEK_END * The offset is set to the size of the file minus offset bytes. * * Return Value: * * Sucess: 0 * * Failure: an int containing -errno */ long long cmyth_ringbuf_seek(cmyth_recorder_t rec, long long offset, int whence) { char msg[128]; int err; int count; long long c; long r; long long ret; cmyth_ringbuf_t ring; if (rec == NULL) return -EINVAL; ring = rec->rec_ring; if ((offset == 0) && (whence == SEEK_CUR)) return ring->file_pos; pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %u[]:[]SEEK_RINGBUF[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d", rec->rec_id, (int32_t)(offset >> 32), (int32_t)(offset & 0xffffffff), whence, (int32_t)(ring->file_pos >> 32), (int32_t)(ring->file_pos & 0xffffffff)); if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(rec->rec_conn); if ((r=cmyth_rcv_long_long(rec->rec_conn, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } switch (whence) { case SEEK_SET: ring->file_pos = offset; break; case SEEK_CUR: ring->file_pos += offset; break; case SEEK_END: ring->file_pos = ring->file_length - offset; break; } ret = ring->file_pos; out: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_ringbuf_read (cmyth_recorder_t rec, char *buf, unsigned long len) * * Scope: PUBLIC * * Description * * Request and read a block of data from backend * * Return Value: * * Sucess: number of bytes transfered * * Failure: an int containing -errno */ int cmyth_ringbuf_read(cmyth_recorder_t rec, char *buf, unsigned long len) { int err, count; int ret, req, nfds; char *end, *cur; char msg[256]; struct timeval tv; fd_set fds; if (!rec) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } pthread_mutex_lock (&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %u[]:[]REQUEST_BLOCK_RINGBUF[]:[]%ld", rec->rec_id, len); if ( (err = cmyth_send_message (rec->rec_conn, msg) ) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } nfds = 0; req = 1; cur = buf; end = buf+len; while (cur < end || req) { tv.tv_sec = 20; tv.tv_usec = 0; FD_ZERO (&fds); if(req) { if((int)rec->rec_conn->conn_fd > nfds) nfds = (int)rec->rec_conn->conn_fd; FD_SET (rec->rec_conn->conn_fd, &fds); } if((int)rec->rec_ring->conn_data->conn_fd > nfds) nfds = (int)rec->rec_ring->conn_data->conn_fd; FD_SET (rec->rec_ring->conn_data->conn_fd, &fds); if ((ret = select (nfds+1, &fds, NULL, NULL,&tv)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: select(() failed (%d)\n", __FUNCTION__, ret); goto out; } if (ret == 0) { rec->rec_ring->conn_data->conn_hang = 1; rec->rec_conn->conn_hang = 1; ret = -ETIMEDOUT; goto out; } /* check control connection */ if (FD_ISSET(rec->rec_conn->conn_fd, &fds) ) { if ((count = cmyth_rcv_length (rec->rec_conn)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } if ((ret = cmyth_rcv_ulong (rec->rec_conn, &err, &len, count))< 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_rcv_long() failed (%d)\n", __FUNCTION__, ret); ret = err; goto out; } rec->rec_ring->file_pos += len; req = 0; end = buf+len; } /* check data connection */ if (FD_ISSET(rec->rec_ring->conn_data->conn_fd, &fds)) { if ((ret = recv (rec->rec_ring->conn_data->conn_fd, cur, end-cur, 0)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: recv() failed (%d)\n", __FUNCTION__, ret); goto out; } cur += ret; } } ret = end - buf; out: pthread_mutex_unlock (&mutex); return ret; }
/* * cmyth_ringbuf_setup(cmyth_recorder_t old_rec) * * Scope: PUBLIC * * Description * * Set up the ring buffer inside a recorder for use in playing live * tv. The recorder is supplied. This will be duplicated and * released, so the caller can re-use the same variable to hold the * return. The new copy of the recorder will have a ringbuffer set up * within it. * * Return Value: * * Success: A pointer to a new recorder structure with a ringbuffer * * Faiure: NULL */ cmyth_recorder_t cmyth_ringbuf_setup(cmyth_recorder_t rec) { static const char service[]="rbuf://"; cmyth_recorder_t new_rec = NULL; char *host = NULL; char *port = NULL; char *path = NULL; char tmp; int err, count; int r; long long size, fill; char msg[256]; char url[1024]; char buf[32]; cmyth_conn_t control; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return NULL; } control = rec->rec_conn; pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_RECORDER %u[]:[]SETUP_RING_BUFFER[]:[]0", rec->rec_id); if ((err=cmyth_send_message(control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } count = cmyth_rcv_length(control); if (control->conn_version >= 16) { r = cmyth_rcv_string(control, &err, buf, sizeof(buf)-1, count); count -= r; } r = cmyth_rcv_string(control, &err, url, sizeof(url)-1, count); count -= r; if ((r=cmyth_rcv_long_long(control, &err, &size, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); goto out; } count -= r; if ((r=cmyth_rcv_long_long(control, &err, &fill, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); goto out; } cmyth_dbg(CMYTH_DBG_DEBUG, "%s: url is: '%s'\n", __FUNCTION__, url); path = url; if (strncmp(url, service, sizeof(service) - 1) == 0) { /* * The URL starts with rbuf://. The rest looks like * <host>:<port>/<filename>. */ host = url + strlen(service); port = strchr(host, ':'); if (!port) { /* * This does not seem to be a proper URL, so just * assume it is a filename, and get out. */ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: 1 port %s, host = %s\n", __FUNCTION__, port, host); goto out; } port = port + 1; path = strchr(port, '/'); if (!path) { /* * This does not seem to be a proper URL, so just * assume it is a filename, and get out. */ cmyth_dbg(CMYTH_DBG_DEBUG, "%s: no path\n", __FUNCTION__); goto out; } } new_rec = cmyth_recorder_dup(rec); if (new_rec == NULL) { cmyth_dbg(CMYTH_DBG_DEBUG, "%s: cannot create recorder\n", __FUNCTION__); goto out; } ref_release(rec); new_rec->rec_ring = cmyth_ringbuf_create(); tmp = *(port - 1); *(port - 1) = '\0'; new_rec->rec_ring->ringbuf_hostname = ref_strdup(host); *(port - 1) = tmp; tmp = *(path); *(path) = '\0'; new_rec->rec_ring->ringbuf_port = atoi(port); *(path) = tmp; new_rec->rec_ring->ringbuf_url = ref_strdup(url); new_rec->rec_ring->ringbuf_size = size; new_rec->rec_ring->ringbuf_fill = fill; out: pthread_mutex_unlock(&mutex); return new_rec; }
static cmyth_conn_t cmyth_conn_connect(char *server, unsigned short port, unsigned buflen, int tcp_rcvbuf, int event) { cmyth_conn_t conn; char announcement[256]; unsigned long tmp_ver; int attempt = 0; top: conn = cmyth_connect(server, port, buflen, tcp_rcvbuf); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, server, port, buflen); return NULL; } /* * Find out what the Myth Protocol Version is for this connection. * Loop around until we get agreement from the server. */ if (attempt == 0) tmp_ver = conn->conn_version; conn->conn_version = tmp_ver; /* * Myth 0.23.1 (Myth 0.23 + fixes) introduced an out of sequence protocol version number (23056) * due to the next protocol version number having already been bumped in trunk. * * http://www.mythtv.org/wiki/Myth_Protocol */ if (tmp_ver >= 62 && tmp_ver != 23056) { // Treat protocol version number 23056 the same as protocol 56 myth_protomap_t *map = protomap; while (map->version != 0 && map->version != tmp_ver) map++; if (map->version == 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } sprintf(announcement, "MYTH_PROTO_VERSION %ld %s", conn->conn_version, map->token); } else { sprintf(announcement, "MYTH_PROTO_VERSION %ld", conn->conn_version); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_version(conn, &tmp_ver) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_version() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_ERROR, "%s: asked for version %ld, got version %ld\n", __FUNCTION__, conn->conn_version, tmp_ver); if (conn->conn_version != tmp_ver) { if (attempt == 1) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: failed to connect with any version\n", __FUNCTION__); goto shut; } attempt = 1; ref_release(conn); goto top; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: agreed on Version %ld protocol\n", __FUNCTION__, conn->conn_version); sprintf(announcement, "ANN Playback %s %d", my_hostname, event); if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } if (cmyth_rcv_okay(conn, "OK") < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_okay() failed\n", __FUNCTION__); goto shut; } /* * All of the downstream code in libcmyth assumes a monotonically increasing version number. * This was not the case for Myth 0.23.1 (0.23 + fixes) where protocol version number 23056 * was used since 57 had already been used in trunk. * * Convert from protocol version number 23056 to version number 56 so subsequent code within * libcmyth uses the same logic for the 23056 protocol as would be used for protocol version 56. */ if (conn->conn_version == 23056) { conn->conn_version = 56; } return conn; shut: ref_release(conn); return NULL; }
/* * cmyth_file_seek(cmyth_file_t file, long long offset, int whence) * * Scope: PUBLIC * * Description * * Seek to a new position in the file based on the value of whence: * SEEK_SET * The offset is set to offset bytes. * SEEK_CUR * The offset is set to the current position plus offset bytes. * SEEK_END * The offset is set to the size of the file minus offset bytes. * * Return Value: * * Sucess: 0 * * Failure: an int containing -errno */ long long cmyth_file_seek(cmyth_file_t file, long long offset, int whence) { char msg[128]; int err; int count; long long c; long r; long long ret; if (file == NULL) return -EINVAL; if ((offset == 0) && (whence == SEEK_CUR)) return file->file_pos; pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_FILETRANSFER %ld[]:[]SEEK[]:[]%d[]:[]%d[]:[]%d[]:[]%d[]:[]%d", file->file_id, (int32_t)(offset >> 32), (int32_t)(offset & 0xffffffff), whence, (int32_t)(file->file_pos >> 32), (int32_t)(file->file_pos & 0xffffffff)); if ((err = cmyth_send_message(file->file_control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } if ((count=cmyth_rcv_length(file->file_control)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } if ((r=cmyth_rcv_long_long(file->file_control, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_long_long() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } switch (whence) { case SEEK_SET: file->file_pos = offset; break; case SEEK_CUR: file->file_pos += offset; break; case SEEK_END: file->file_pos = file->file_length - offset; break; } ret = file->file_pos; out: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_conn_connect_path(char* path, cmyth_conn_t control, * unsigned buflen, int tcp_rcvbuf) * * Scope: PUBLIC * * Description: * * Create a file structure containing a data connection for use * transfering a file within the MythTV protocol. Return a pointer to * the newly created file structure. The connection in the file * structure is returned held as is the file structure itself. The * connection will be released when the file structure is released. * The file structure can be released using ref_release(). * * Return Value: * * Success: Non-NULL cmyth_file_t (this is a pointer type) * * Failure: NULL cmyth_file_t */ cmyth_file_t cmyth_conn_connect_path(char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf) { cmyth_conn_t conn = NULL; char *announcement = NULL; char reply[16]; char host[256]; int err = 0; int count = 0; int r, port; int ann_size = sizeof("ANN FileTransfer []:[][]:[]"); struct sockaddr_in addr; socklen_t addr_size = sizeof(addr); cmyth_file_t ret = NULL; if (getpeername(control->conn_fd, (struct sockaddr*)&addr, &addr_size)<0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: getpeername() failed\n", __FUNCTION__); goto shut; } inet_ntop(addr.sin_family, &addr.sin_addr, host, sizeof(host)); port = ntohs(addr.sin_port); ret = cmyth_file_create(control); if (!ret) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_file_create() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", __FUNCTION__); conn = cmyth_connect(host, port, buflen, tcp_rcvbuf); cmyth_dbg(CMYTH_DBG_PROTO, "%s: done connecting data connection, conn = %p\n", __FUNCTION__, conn); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, host, port, buflen); goto shut; } ann_size += strlen(path) + strlen(my_hostname); announcement = malloc(ann_size); if (!announcement) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc(%d) failed for announcement\n", __FUNCTION__, ann_size); goto shut; } if (control->conn_version >= 44) { sprintf(announcement, "ANN FileTransfer %s[]:[]%s[]:[]", my_hostname, path); } else { sprintf(announcement, "ANN FileTransfer %s[]:[]%s", my_hostname, path); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } ret->file_data = ref_hold(conn); count = cmyth_rcv_length(conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto shut; } reply[sizeof(reply) - 1] = '\0'; r = cmyth_rcv_string(conn, &err, reply, sizeof(reply) - 1, count); if (err != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); goto shut; } if (strcmp(reply, "OK") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: reply ('%s') is not 'OK'\n", __FUNCTION__, reply); goto shut; } count -= r; r = cmyth_rcv_long(conn, &err, &ret->file_id, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (id) cmyth_rcv_long() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; r = cmyth_rcv_u_long_long(conn, &err, &ret->file_length, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (length) cmyth_rcv_longlong() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; free(announcement); ref_release(conn); return ret; shut: if (announcement) { free(announcement); } ref_release(ret); ref_release(conn); return NULL; }
/* * cmyth_conn_connect_path(char* path, cmyth_conn_t control, * unsigned buflen, int tcp_rcvbuf) * * Scope: PUBLIC * * Description: * * Create a file structure containing a data connection for use * transfering a file within the MythTV protocol. Return a pointer to * the newly created file structure. The connection in the file * structure is returned held as is the file structure itself. The * connection will be released when the file structure is released. * The file structure can be released using ref_release(). * * Return Value: * * Success: Non-NULL cmyth_file_t (this is a pointer type) * * Failure: NULL cmyth_file_t */ cmyth_file_t cmyth_conn_connect_path(char* path, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf, char* storage_group) { cmyth_conn_t conn = NULL; char *announcement = NULL; char reply[16]; char host[256]; int err = 0; int count = 0; int r, port; int ann_size = sizeof("ANN FileTransfer 0[]:[][]:[]"); struct sockaddr_in addr; socklen_t addr_size = sizeof(addr); cmyth_file_t ret = NULL; if (getpeername(control->conn_fd, (struct sockaddr*)&addr, &addr_size)<0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: getpeername() failed\n", __FUNCTION__); goto shut; } inet_ntop(addr.sin_family, &addr.sin_addr, host, sizeof(host)); port = ntohs(addr.sin_port); ret = cmyth_file_create(control); if (!ret) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_file_create() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", __FUNCTION__); conn = cmyth_connect(host, port, buflen, tcp_rcvbuf); cmyth_dbg(CMYTH_DBG_PROTO, "%s: done connecting data connection, conn = %p\n", __FUNCTION__, conn); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, host, port, buflen); goto shut; } /* * Explicitly set the conn version to the control version as cmyth_connect() doesn't and some of * the cmyth_rcv_* functions expect it to be the same as the protocol version used by mythbackend. */ conn->conn_version = control->conn_version; ann_size += strlen(path) + strlen(my_hostname) + strlen(storage_group) + 6; announcement = malloc(ann_size); if (!announcement) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc(%d) failed for announcement\n", __FUNCTION__, ann_size); goto shut; } if (control->conn_version >= 44) { /*TSP: from version 44 according to the source code*/ if (strlen(storage_group) > 1) { sprintf(announcement, "ANN FileTransfer %s 0 0 0[]:[]%s[]:[]%s", my_hostname, path, storage_group); } else { sprintf(announcement, "ANN FileTransfer %s 0[]:[]%s[]:[]", // write = false my_hostname, path); } } else { sprintf(announcement, "ANN FileTransfer %s[]:[]%s", my_hostname, path); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } ret->file_data = ref_hold(conn); count = cmyth_rcv_length(conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto shut; } reply[sizeof(reply) - 1] = '\0'; r = cmyth_rcv_string(conn, &err, reply, sizeof(reply) - 1, count); if (err != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); goto shut; } if (strcmp(reply, "OK") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: reply ('%s') is not 'OK'\n", __FUNCTION__, reply); goto shut; } count -= r; r = cmyth_rcv_long(conn, &err, &ret->file_id, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (id) cmyth_rcv_long() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; r = cmyth_rcv_uint64(conn, &err, &ret->file_length, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (length) cmyth_rcv_uint64() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; free(announcement); ref_release(conn); return ret; shut: if (announcement) { free(announcement); } ref_release(ret); ref_release(conn); return NULL; }
/* * cmyth_proglist_get_list() * * Scope: PRIVATE (static) * * Description * * Obtain a program list from the query specified in 'msg' from the * function 'func'. Make the query on 'conn' and put the results in * 'proglist'. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ static int cmyth_proglist_get_list(cmyth_conn_t conn, cmyth_proglist_t proglist, char *msg, const char *func) { int err = 0; int count; int ret; if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", func); return -EINVAL; } if (!proglist) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program list\n", func); return -EINVAL; } pthread_mutex_lock(&conn->conn_mutex); if ((err = cmyth_send_message(conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", func, err); ret = err; goto out; } count = cmyth_rcv_length(conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", func, count); ret = count; goto out; } if (strcmp(msg, "QUERY_GETALLPENDING") == 0) { int32_t c; int r; if ((r = cmyth_rcv_int32(conn, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } count -= r; } if (cmyth_rcv_proglist(conn, &err, proglist, count) != count) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_proglist() < count\n", func); } if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_proglist() failed (%d)\n", func, err); ret = -1 * err; goto out; } ret = 0; out: pthread_mutex_unlock(&conn->conn_mutex); return ret; }
char * cmyth_conn_get_backend_hostname(cmyth_conn_t conn) { int count, err; char* result = NULL; pthread_mutex_lock(&mutex); if(conn->conn_version < 17) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support QUERY_HOSTNAME\n", __FUNCTION__); return NULL; } if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } if ((err = cmyth_send_message(conn, "QUERY_HOSTNAME")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto err; } if ((count=cmyth_rcv_length(conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto err; } result = ref_alloc(count+1); count -= cmyth_rcv_string(conn, &err, result, count, count); if (err < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); goto err; } while(count > 0 && !err) { char buffer[100]; count -= cmyth_rcv_string(conn, &err, buffer, sizeof(buffer)-1, count); buffer[sizeof(buffer)-1] = 0; cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer); } pthread_mutex_unlock(&mutex); if(!strcmp("-1",result)) { cmyth_dbg(CMYTH_DBG_PROTO, "%s: Failed to retrieve backend hostname.\n", __FUNCTION__); return NULL; } return result; err: pthread_mutex_unlock(&mutex); if(result) ref_release(result); return NULL; }
cmyth_storagegroup_file_t cmyth_storagegroup_get_fileinfo(cmyth_conn_t control, char *storagegroup, char *hostname, char *filename) { char msg[256]; int count = 0; int err = 0; cmyth_storagegroup_file_t ret = NULL; int consumed = 0; char tmp_str[2048]; if (!control) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return 0; } pthread_mutex_lock(&control->conn_mutex); snprintf(msg, sizeof(msg), "QUERY_SG_FILEQUERY[]:[]%s[]:[]%s[]:[]%s", hostname, storagegroup, filename); err = cmyth_send_message(control, msg); if (err < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } count = cmyth_rcv_length(control); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto out; } consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); ret = NULL; goto out; } else if (count == 0) { cmyth_dbg(CMYTH_DBG_WARN, "%s: QUERY_SG_FILEQUERY failed(%s)\n", __FUNCTION__, tmp_str); ret = NULL; goto out; } ret = cmyth_storagegroup_file_create(); if (!ret) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: alloc() failed for file\n", __FUNCTION__); ref_release(ret); ret = NULL; goto out; } ret->filename = ref_strdup(tmp_str); consumed = cmyth_rcv_string(control, &err, tmp_str, sizeof(tmp_str) - 1, count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); ref_release(ret); ret = NULL; goto out; } ret->lastmodified = atol(tmp_str); consumed = cmyth_rcv_new_int64(control, &err, &(ret->size), count, 1); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_int64() failed (%d)\n", __FUNCTION__, count); ref_release(ret); ret = NULL; goto out; } cmyth_dbg(CMYTH_DBG_DEBUG, "%s: filename: %s\n", __FUNCTION__, ret->filename); out: pthread_mutex_unlock(&control->conn_mutex); return ret; }
/* * cmyth_conn_reschedule_recordings(cmyth_conn_t rec, int recordid) * * Scope: PUBLIC * * Description * * Issues a run of the re-scheduler. * Takes an optional recordid, or -1 performs a full run. * * Return Value: * * Success: 0 * * Failure: -(ERRNO) */ int cmyth_conn_reschedule_recordings(cmyth_conn_t conn, int recordid) { int err = 0; int id; char msg[256]; if (conn->conn_version < 15) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support RESCHEDULE_RECORDINGS\n", __FUNCTION__); return -1; } /* * RESCHEDULE_RECORDINGS changed in protocol version 73: * * MATCH reschedule requests should be used when the guide data or a * specific recording rule is changed. The syntax is as follows. * * MATCH <recordid> <sourceid> <mplexid> <maxstarttime> <reason> * * CHECK reschedule requests should be used when the status of a * specific episode is affected such as when "never record" or "allow * re-record" are selected or a recording finishes or is deleted. The * syntax is as follows. * * CHECK <recstatus> <recordid> <findid> <reason> * <title> * <subtitle> * <description> * <programid> */ if (conn->conn_version < 73) { id = (recordid > 0 ? recordid : -1); snprintf(msg, sizeof(msg), "RESCHEDULE_RECORDINGS %i", id); } else { if (recordid == 0) { strncpy(msg, "RESCHEDULE_RECORDINGS []:[]CHECK 0 0 0 cmyth[]:[][]:[][]:[][]:[]**any**", sizeof(msg)); } else { id = (recordid > 0 ? recordid : 0); snprintf(msg, sizeof(msg), "RESCHEDULE_RECORDINGS []:[]MATCH %i 0 0 - cmyth", id); } } pthread_mutex_lock(&mutex); if ((err = cmyth_send_message(conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto out; } if ((err=cmyth_rcv_feedback(conn, "1")) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_feedback() failed (%d)\n", __FUNCTION__, err); goto out; } out: pthread_mutex_unlock(&mutex); return err; }
/* * cmyth_recorder_get_next_program_info(cmyth_recorder_t rec, * cmyth_proginfo_t proginfo, * cmyth_browsedir_t direction) * * Scope: PRIVATE (static) * * Description: * * Request program information from the recorder 'rec' for the next * program in the program guide from the current program (i.e. current * channel and time slot) in the direction specified by 'direction' * which may have any of the following values: * * BROWSE_DIRECTION_SAME - Stay in the same place * BROWSE_DIRECTION_UP - Move up one slot (down one channel) * BROWSE_DIRECTION_DOWN - Move down one slot (up one channel) * BROWSE_DIRECTION_LEFT - Move left one slot (down one time slot) * BROWSE_DIRECTION_RIGHT - Move right one slot (up one time slot) * BROWSE_DIRECTION_FAVORITE - Move to the next favorite slot * * The program information will be used to fill out 'proginfo'. * * This does not affect the current recording. * * Return Value: * * Success: 1 - valid channel, 0 - invalid channel * * Failure: -(ERRNO) */ int cmyth_recorder_get_next_program_info(cmyth_recorder_t rec, cmyth_proginfo_t cur_prog, cmyth_proginfo_t next_prog, cmyth_browsedir_t direction) { int err, count; int ret = -ENOSYS; char msg[256]; char title[256], subtitle[256], desc[256], category[256]; char callsign[256], iconpath[256]; char channelname[256], chanid[256], seriesid[256], programid[256]; char date[256]; struct tm *tm; time_t t; cmyth_conn_t control; if (!rec) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return -ENOSYS; } control = rec->rec_conn; pthread_mutex_lock(&mutex); t = time(NULL); tm = localtime(&t); snprintf(date, sizeof(date), "%.4d%.2d%.2d%.2d%.2d%.2d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); snprintf(msg, sizeof(msg), "QUERY_RECORDER %d[]:[]GET_NEXT_PROGRAM_INFO[]:[]%s[]:[]%ld[]:[]%i[]:[]%s", rec->rec_id, cur_prog->proginfo_channame, cur_prog->proginfo_chanId, direction, date); if ((err=cmyth_send_message(control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(control); count -= cmyth_rcv_string(control, &err, title, sizeof(title), count); count -= cmyth_rcv_string(control, &err, subtitle, sizeof(subtitle), count); count -= cmyth_rcv_string(control, &err, desc, sizeof(desc), count); count -= cmyth_rcv_string(control, &err, category, sizeof(category), count); count -= cmyth_rcv_timestamp(control, &err, &next_prog->proginfo_start_ts, count); count -= cmyth_rcv_timestamp(control, &err, &next_prog->proginfo_end_ts, count); count -= cmyth_rcv_string(control, &err, callsign, sizeof(callsign), count); count -= cmyth_rcv_string(control, &err, iconpath, sizeof(iconpath), count); count -= cmyth_rcv_string(control, &err, channelname, sizeof(channelname), count); count -= cmyth_rcv_string(control, &err, chanid, sizeof(chanid), count); if (control->conn_version >= 12) { count -= cmyth_rcv_string(control, &err, seriesid, sizeof(seriesid), count); count -= cmyth_rcv_string(control, &err, programid, sizeof(programid), count); } if (count != 0) { ret = -1; goto out; } /* * if the program info is blank, return an error */ if ((strlen(title) == 0) && (strlen(subtitle) == 0) && (strlen(desc) == 0) && (strlen(channelname) == 0) && (strlen(chanid) == 0)) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: blank channel found\n", __FUNCTION__); ret = -1; goto out; } next_prog->proginfo_title = ref_strdup(title); next_prog->proginfo_subtitle = ref_strdup(subtitle); next_prog->proginfo_description = ref_strdup(desc); next_prog->proginfo_channame = ref_strdup(channelname); next_prog->proginfo_chansign = ref_strdup(callsign); next_prog->proginfo_chanId = atoi(chanid); ref_hold(next_prog->proginfo_start_ts); ref_hold(next_prog->proginfo_end_ts); ret = 0; out: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_file_seek() * * Scope: PUBLIC * * Description * * Seek to a new position in the file based on the value of whence: * WHENCE_SET * The offset is set to offset bytes. * WHENCE_CUR * The offset is set to the current position plus offset bytes. * WHENCE_END * The offset is set to the size of the file minus offset bytes. * * Return Value: * * Sucess: 0 * * Failure: an int containing -errno */ int64_t cmyth_file_seek(cmyth_file_t file, int64_t offset, int8_t whence) { char msg[128]; int err; int count; int64_t c; int r; int64_t ret; if (file == NULL) return -EINVAL; if ((offset == 0) && (whence == WHENCE_CUR)) return file->file_pos; if ((offset == file->file_pos) && (whence == WHENCE_SET)) return file->file_pos; pthread_mutex_lock(&mutex); ret = 0; while(file->file_pos < file->file_req) { c = file->file_req - file->file_pos; if(c > sizeof(msg)) c = sizeof(msg); if ((ret = cmyth_file_get_block(file, msg, (size_t)c)) < 0) break; } if (ret < 0) goto out; if (file->file_control->conn_version >= 66) { /* * Since protocol 66 mythbackend expects to receive a single 64 bit integer rather than * two 32 bit hi and lo integers. */ snprintf(msg, sizeof(msg), "QUERY_FILETRANSFER %"PRIu32"[]:[]SEEK[]:[]%"PRId64"[]:[]%"PRId8"[]:[]%"PRId64, file->file_id, offset, whence, file->file_pos); } else { snprintf(msg, sizeof(msg), "QUERY_FILETRANSFER %"PRIu32"[]:[]SEEK[]:[]%"PRId32"[]:[]%"PRId32"[]:[]%"PRId8"[]:[]%"PRId32"[]:[]%"PRId32, file->file_id, (int32_t)(offset >> 32), (int32_t)(offset & 0xffffffff), whence, (int32_t)(file->file_pos >> 32), (int32_t)(file->file_pos & 0xffffffff)); } if ((err = cmyth_send_message(file->file_control, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } if ((count=cmyth_rcv_length(file->file_control)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } if ((r=cmyth_rcv_int64(file->file_control, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_int64() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } file->file_pos = c; file->file_req = file->file_pos; if(file->file_pos > file->file_length) file->file_length = file->file_pos; ret = file->file_pos; out: pthread_mutex_unlock(&mutex); return ret; }
/* * cmyth_conn_get_free_recorder(cmyth_conn_t control, cmyth_recorder_t rec) * * * Scope: PUBLIC * * Description * * Obtain the next available free recorder the connection specified by * 'control'. This fills out the recorder structure specified by 'rec'. * * Return Value: * * Success: 0 for not complete, 1 for complete * * Failure: -(errno) */ cmyth_recorder_t cmyth_conn_get_free_recorder(cmyth_conn_t conn) { int err, count; int r; long port, id; char msg[256]; char reply[256]; cmyth_recorder_t rec = NULL; if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } pthread_mutex_lock(&mutex); if ((rec=cmyth_recorder_create()) == NULL) goto fail; snprintf(msg, sizeof(msg), "GET_FREE_RECORDER"); if ((err = cmyth_send_message(conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } count = cmyth_rcv_length(conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto fail; } if ((r=cmyth_rcv_long(conn, &err, &id, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_long() failed (%d)\n", __FUNCTION__, r); goto fail; } count -= r; if ((r=cmyth_rcv_string(conn, &err, reply, sizeof(reply)-1, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, r); goto fail; } count -= r; if ((r=cmyth_rcv_long(conn, &err, &port, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_long() failed (%d)\n", __FUNCTION__, r); goto fail; } if (port == -1) goto fail; rec->rec_id = id; rec->rec_server = ref_strdup(reply); rec->rec_port = port; if (cmyth_conn_connect_recorder(rec, conn->conn_buflen, conn->conn_tcp_rcvbuf) < 0) goto fail; pthread_mutex_unlock(&mutex); return rec; fail: if (rec) ref_release(rec); pthread_mutex_unlock(&mutex); return NULL; }
/* * cmyth_file_read() * * Scope: PUBLIC * * Description * * Request and read a block of data from backend * * Return Value: * * Sucess: number of bytes transfered * * Failure: an int containing -errno */ int32_t cmyth_file_read(cmyth_file_t file, char *buf, int32_t len) { int err, count; int32_t ret; int req, nfds, rec; char *end, *cur; char msg[256]; int64_t len64; struct timeval tv; fd_set fds; if (!file || !file->file_data) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return -EINVAL; } if (len == 0) return 0; if(len > file->file_data->conn_tcp_rcvbuf) len = file->file_data->conn_tcp_rcvbuf; pthread_mutex_lock (&mutex); /* make sure we have outstanding requests that fill the buffer that was called with */ /* this way we should be able to saturate the network connection better */ if (file->file_req < file->file_pos + len) { snprintf (msg, sizeof (msg), "QUERY_FILETRANSFER %"PRIu32"[]:[]REQUEST_BLOCK[]:[]%"PRId32, file->file_id, (int32_t)(file->file_pos + len - file->file_req)); if ( (err = cmyth_send_message (file->file_control, msg) ) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } req = 1; } else { req = 0; } rec = 0; cur = buf; end = buf+len; while (cur == buf || req || rec) { if(rec) { tv.tv_sec = 0; tv.tv_usec = 0; } else { tv.tv_sec = 20; tv.tv_usec = 0; } nfds = 0; FD_ZERO (&fds); if (req) { if ((int)file->file_control->conn_fd > nfds) nfds = (int)file->file_control->conn_fd; FD_SET (file->file_control->conn_fd, &fds); } if ((int)file->file_data->conn_fd > nfds) nfds = (int)file->file_data->conn_fd; FD_SET (file->file_data->conn_fd, &fds); if ((ret = select (nfds+1, &fds, NULL, NULL,&tv)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: select(() failed (%d)\n", __FUNCTION__, ret); goto out; } if (ret == 0 && !rec) { file->file_control->conn_hang = 1; file->file_data->conn_hang = 1; ret = -ETIMEDOUT; goto out; } /* check control connection */ if (FD_ISSET(file->file_control->conn_fd, &fds)) { if ((count=cmyth_rcv_length (file->file_control)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); ret = count; goto out; } /* * MythTV originally sent back a signed 32bit value but was changed to a * signed 64bit value in http://svn.mythtv.org/trac/changeset/18011 (1-Aug-2008). * * libcmyth now retrieves the 64-bit signed value, does error-checking, * and then converts to a 32bit signed. * * This rcv_ method needs to be forced to use new_int64 to pull back a * single 64bit number otherwise the handling in rcv_int64 will revert to * the old two 32bit hi and lo long values. */ if ((ret = cmyth_rcv_new_int64(file->file_control, &err, &len64, count, 1))< 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: cmyth_rcv_new_int64() failed (%d)\n", __FUNCTION__, ret); ret = err; goto out; } if (len64 > 0x7fffffff || len64 < 0) { /* -1 seems to be a common result, but isn't valid so use 0 instead. */ cmyth_dbg (CMYTH_DBG_WARN, "%s: cmyth_rcv_new_int64() returned out of bound value (%"PRId64"). Using 0 instead.\n", __FUNCTION__, len64); len64 = 0; } len = (int32_t)len64; req = 0; file->file_req += len; if (file->file_req < file->file_pos) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: received invalid invalid length, read position is ahead of request (req: %"PRId64", pos: %"PRId64", len: %"PRId64")\n", __FUNCTION__, file->file_req, file->file_pos, len64); ret = -1; goto out; } /* check if we are already done */ if (file->file_pos == file->file_req) break; } /* restore direct request fleg */ rec = 0; /* check data connection */ if (FD_ISSET(file->file_data->conn_fd, &fds)) { if (end < cur) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: positions invalid on read, bailing out (cur: %x, end: %x)\n", __FUNCTION__, cur, end); ret = -1; goto out; } if ((ret = recv (file->file_data->conn_fd, cur, (int32_t)(end - cur), 0)) < 0) { cmyth_dbg (CMYTH_DBG_ERROR, "%s: recv() failed (%d)\n", __FUNCTION__, ret); goto out; } cur += ret; file->file_pos += ret; if(ret) rec = 1; /* attempt to read directly again to get all queued packets */ } } /* make sure file grows, as we move past length */ if (file->file_pos > file->file_length) file->file_length = file->file_pos; ret = (int32_t)(cur - buf); out: pthread_mutex_unlock (&mutex); return ret; }
char * cmyth_conn_get_setting(cmyth_conn_t conn, const char* hostname, const char* setting) { char msg[256]; int count, err; char* result = NULL; if(conn->conn_version < 17) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: protocol version doesn't support QUERY_SETTING\n", __FUNCTION__); return NULL; } if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no connection\n", __FUNCTION__); return NULL; } pthread_mutex_lock(&mutex); snprintf(msg, sizeof(msg), "QUERY_SETTING %s %s", hostname, setting); if ((err = cmyth_send_message(conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto err; } if ((count=cmyth_rcv_length(conn)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto err; } result = ref_alloc(count+1); count -= cmyth_rcv_string(conn, &err, result, count, count); if (err < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); goto err; } while(count > 0 && !err) { char buffer[100]; count -= cmyth_rcv_string(conn, &err, buffer, sizeof(buffer)-1, count); buffer[sizeof(buffer)-1] = 0; cmyth_dbg(CMYTH_DBG_ERROR, "%s: odd left over data %s\n", __FUNCTION__, buffer); } pthread_mutex_unlock(&mutex); return result; err: if(result) ref_release(result); pthread_mutex_unlock(&mutex); return NULL; }
static int proginfo_command(cmyth_conn_t control, cmyth_proginfo_t prog, char *cmd, long *result) { long c = 0; char *buf; unsigned int len = ((2 * CMYTH_LONGLONG_LEN) + (6 * CMYTH_TIMESTAMP_LEN) + (16 * CMYTH_LONG_LEN)); char start_ts[CMYTH_TIMESTAMP_LEN + 1]; char end_ts[CMYTH_TIMESTAMP_LEN + 1]; char rec_start_ts[CMYTH_TIMESTAMP_LEN + 1]; char rec_end_ts[CMYTH_TIMESTAMP_LEN + 1]; char originalairdate[CMYTH_TIMESTAMP_LEN + 1]; char lastmodified[CMYTH_TIMESTAMP_LEN + 1]; int err = 0; int count = 0; long r = 0; int ret = 0; if (!prog) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no program info\n", __FUNCTION__); return -EINVAL; } len += strlen(prog->proginfo_title); len += strlen(prog->proginfo_subtitle); len += strlen(prog->proginfo_description); len += strlen(prog->proginfo_category); len += strlen(prog->proginfo_chanstr); len += strlen(prog->proginfo_chansign); len += strlen(prog->proginfo_channame); len += strlen(prog->proginfo_url); len += strlen(prog->proginfo_hostname); len += strlen(prog->proginfo_playgroup); len += strlen(prog->proginfo_seriesid); len += strlen(prog->proginfo_programid); if (prog->proginfo_inetref) { len += strlen(prog->proginfo_inetref); } if (prog->proginfo_recpriority_2) { len += strlen(prog->proginfo_recpriority_2); } if (prog->proginfo_storagegroup) { len += strlen(prog->proginfo_storagegroup); } buf = alloca(len + 1+2048); if (!buf) { return -ENOMEM; } if(control->conn_version < 14) { cmyth_timestamp_to_string(start_ts, prog->proginfo_start_ts); cmyth_timestamp_to_string(end_ts, prog->proginfo_end_ts); cmyth_timestamp_to_string(rec_start_ts, prog->proginfo_rec_start_ts); cmyth_timestamp_to_string(rec_end_ts, prog->proginfo_rec_end_ts); cmyth_timestamp_to_string(originalairdate, prog->proginfo_originalairdate); cmyth_timestamp_to_string(lastmodified, prog->proginfo_lastmodified); } else { cmyth_datetime_to_string(start_ts, prog->proginfo_start_ts); cmyth_datetime_to_string(end_ts, prog->proginfo_end_ts); cmyth_datetime_to_string(rec_start_ts, prog->proginfo_rec_start_ts); cmyth_datetime_to_string(rec_end_ts, prog->proginfo_rec_end_ts); cmyth_datetime_to_string(originalairdate, prog->proginfo_originalairdate); cmyth_datetime_to_string(lastmodified, prog->proginfo_lastmodified); } if(control->conn_version > 32) { cmyth_timestamp_to_isostring(originalairdate, prog->proginfo_originalairdate); } if(control->conn_version < 12) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: delete not supported with protocol ver %d\n", __FUNCTION__, control->conn_version); return -EINVAL; } sprintf(buf, "%s 0[]:[]", cmd); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_title); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_subtitle); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_description); if (control->conn_version >= 67) { sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_season); sprintf(buf + strlen(buf), "%u[]:[]", prog->proginfo_episode); } sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_category); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_chanId); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_chanstr); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_chansign); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_channame); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_url); if (control->conn_version >= 57) { sprintf(buf + strlen(buf), "%"PRId64"[]:[]", prog->proginfo_Length); } else { sprintf(buf + strlen(buf), "%d[]:[]", (int32_t)(prog->proginfo_Length >> 32)); sprintf(buf + strlen(buf), "%d[]:[]", (int32_t)(prog->proginfo_Length & 0xffffffff)); } sprintf(buf + strlen(buf), "%s[]:[]", start_ts); sprintf(buf + strlen(buf), "%s[]:[]", end_ts); if (control->conn_version < 57) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_unknown_0); // "duplicate" sprintf(buf + strlen(buf), "%ld[]:[]", 0L); // "shareable" } sprintf(buf + strlen(buf), "%ld[]:[]", 0L); // "findid" sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_hostname); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_source_id); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_card_id); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_input_id); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_rec_priority); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_rec_status); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_record_id); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_rec_type); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_rec_dups); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_unknown_1); // "dupmethod" sprintf(buf + strlen(buf), "%s[]:[]", rec_start_ts); sprintf(buf + strlen(buf), "%s[]:[]", rec_end_ts); if (control->conn_version < 57) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_repeat); } sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_program_flags); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_recgroup); if (control->conn_version < 57) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_chancommfree); } sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_chan_output_filters); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_seriesid); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_programid); if (control->conn_version >= 67) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_inetref); } sprintf(buf + strlen(buf), "%s[]:[]", lastmodified); sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_stars); sprintf(buf + strlen(buf), "%s[]:[]", originalairdate); if (control->conn_version >= 15 && control->conn_version < 57) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_hasairdate); } if (control->conn_version >= 18) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_playgroup); } if (control->conn_version >= 25) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_recpriority_2); } if (control->conn_version >= 31) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_parentid); } if (control->conn_version >= 32) { sprintf(buf + strlen(buf), "%s[]:[]", prog->proginfo_storagegroup); } if (control->conn_version >= 35) { sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_audioproperties); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_videoproperties); sprintf(buf + strlen(buf), "%ld[]:[]", prog->proginfo_subtitletype); } if (control->conn_version >= 43) { sprintf(buf + strlen(buf), "%d[]:[]", prog->proginfo_year); } pthread_mutex_lock(&control->conn_mutex); if ((err = cmyth_send_message(control, buf)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); ret = err; goto out; } count = cmyth_rcv_length(control); if ((r=cmyth_rcv_long(control, &err, &c, count)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, r); ret = err; goto out; } if (result) { *result = c; } out: pthread_mutex_unlock(&control->conn_mutex); return ret; }
/* * cmyth_conn_connect_file(char *server, unsigned short port, unsigned buflen * cmyth_proginfo_t prog) * * Scope: PUBLIC * * Description: * * Create a file structure containing a data connection for use * transfering a file within the MythTV protocol. Return a pointer to * the newly created file structure. The connection in the file * structure is returned held as is the file structure itself. The * connection will be released when the file structure is released. * The file structure can be released using ref_release(). * * Return Value: * * Success: Non-NULL cmyth_file_t (this is a pointer type) * * Failure: NULL cmyth_file_t */ cmyth_file_t cmyth_conn_connect_file(cmyth_proginfo_t prog, cmyth_conn_t control, unsigned buflen, int tcp_rcvbuf) { cmyth_conn_t conn = NULL; char *announcement = NULL; char *myth_host = NULL; char reply[16]; int err = 0; int count = 0; int r; int ann_size = sizeof("ANN FileTransfer []:[][]:[]"); cmyth_file_t ret = NULL; if (!prog) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: prog is NULL\n", __FUNCTION__); goto shut; } if (!prog->proginfo_host) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: prog host is NULL\n", __FUNCTION__); goto shut; } if (!prog->proginfo_pathname) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: prog has no pathname in it\n", __FUNCTION__); goto shut; } ret = cmyth_file_create(control); if (!ret) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_file_create() failed\n", __FUNCTION__); goto shut; } cmyth_dbg(CMYTH_DBG_PROTO, "%s: connecting data connection\n", __FUNCTION__); if (control->conn_version >= 17) { myth_host = cmyth_conn_get_setting(control, prog->proginfo_host, "BackendServerIP"); } if (!myth_host) { cmyth_dbg(CMYTH_DBG_PROTO, "%s: BackendServerIP setting not found. Using proginfo_host: %s\n", __FUNCTION__, prog->proginfo_host); myth_host = ref_alloc(strlen(prog->proginfo_host) + 1); strcpy(myth_host, prog->proginfo_host); } conn = cmyth_connect(myth_host, prog->proginfo_port, buflen, tcp_rcvbuf); cmyth_dbg(CMYTH_DBG_PROTO, "%s: done connecting data connection, conn = %d\n", __FUNCTION__, conn); if (!conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_connect(%s, %d, %d) failed\n", __FUNCTION__, myth_host, prog->proginfo_port, buflen); goto shut; } ann_size += strlen(prog->proginfo_pathname) + strlen(my_hostname); announcement = malloc(ann_size); if (!announcement) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: malloc(%d) failed for announcement\n", __FUNCTION__, ann_size); goto shut; } if (control->conn_version >= 44) { sprintf(announcement, "ANN FileTransfer %s[]:[]%s[]:[]", my_hostname, prog->proginfo_pathname); } else { sprintf(announcement, "ANN FileTransfer %s[]:[]%s", my_hostname, prog->proginfo_pathname); } if (cmyth_send_message(conn, announcement) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message('%s') failed\n", __FUNCTION__, announcement); goto shut; } ret->file_data = ref_hold(conn); count = cmyth_rcv_length(conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto shut; } reply[sizeof(reply) - 1] = '\0'; r = cmyth_rcv_string(conn, &err, reply, sizeof(reply) - 1, count); if (err != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, err); goto shut; } if (strcmp(reply, "OK") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: reply ('%s') is not 'OK'\n", __FUNCTION__, reply); goto shut; } count -= r; r = cmyth_rcv_long(conn, &err, &ret->file_id, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (id) cmyth_rcv_long() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; r = cmyth_rcv_u_long_long(conn, &err, &ret->file_length, count); if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: (length) cmyth_rcv_longlong() failed (%d)\n", __FUNCTION__, err); goto shut; } count -= r; free(announcement); ref_release(conn); ref_release(myth_host); return ret; shut: if (announcement) { free(announcement); } ref_release(ret); ref_release(conn); ref_release(myth_host); return NULL; }
/* * cmyth_recorder_get_duration_map() * * Scope: PUBLIC * * Description * * Request a list of {keynum, duration} pairs starting at keynum * 'start' and ending with keynum 'end' from the current recording on * recorder 'rec'. * * Return Value: * * Success: A non-NULL, held cmyth_posmap_t * Failure: A NULL pointer */ cmyth_posmap_t cmyth_recorder_get_duration_map(cmyth_recorder_t rec, uint32_t start, uint32_t end) { int err, count, consumed; cmyth_posmap_t ret = NULL; char msg[256]; char tmp[1024]; if (!rec || !rec->rec_conn) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: no recorder connection\n", __FUNCTION__); return NULL; } pthread_mutex_lock(&rec->rec_conn->conn_mutex); if (rec->rec_conn->conn_version >= 77) { snprintf(msg, sizeof(msg), "QUERY_RECORDER %"PRIu32"[]:[]FILL_DURATION_MAP[]:[]%"PRIu32"[]:[]%"PRIu32, rec->rec_id, start, end); if ((err = cmyth_send_message(rec->rec_conn, msg)) < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_send_message() failed (%d)\n", __FUNCTION__, err); goto fail; } count = cmyth_rcv_length(rec->rec_conn); if (count < 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); goto fail; } ret = cmyth_posmap_create(); if (ret == NULL) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_posmap_create() failed\n", __FUNCTION__); goto fail; } if (count > 2) { consumed = cmyth_rcv_posmap(rec->rec_conn, &err, ret, count); count -= consumed; if (err) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_posmap() failed (%d)\n", __FUNCTION__, err); ref_release(ret); ret = NULL; } } err = 0; while(count > 0 && err == 0) { consumed = cmyth_rcv_data(rec->rec_conn, &err, (unsigned char*)tmp, sizeof(tmp) - 1, count); cmyth_dbg(CMYTH_DBG_ERROR, "%s: leftover data: count %i, read %i, errno %i\n", __FUNCTION__, count, consumed, err); count -= consumed; } } fail: pthread_mutex_unlock(&rec->rec_conn->conn_mutex); return ret; }