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; }
static char * cmyth_conn_get_setting_unlocked(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; } 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); } if(!strcmp("-1",result)) { cmyth_dbg(CMYTH_DBG_PROTO, "%s: Setting: %s or hostname: %s not found.\n", __FUNCTION__, setting,hostname); return NULL; } return result; err: if(result) ref_release(result); 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; }
/* * 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_event_t cmyth_event_get(cmyth_conn_t conn, char * data, int len) { int count, err, consumed, i; char tmp[1024]; cmyth_event_t event; cmyth_proginfo_t proginfo = NULL; if (conn == NULL) goto fail; if ((count=cmyth_rcv_length(conn)) <= 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_length() failed (%d)\n", __FUNCTION__, count); return CMYTH_EVENT_CLOSE; } consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); count -= consumed; if (strcmp(tmp, "BACKEND_MESSAGE") != 0) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_rcv_string() failed (%d)\n", __FUNCTION__, count); goto fail; } consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); count -= consumed; if (strcmp(tmp, "RECORDING_LIST_CHANGE") == 0) { event = CMYTH_EVENT_RECORDING_LIST_CHANGE; } else if (strncmp(tmp, "RECORDING_LIST_CHANGE ADD", 25) == 0) { event = CMYTH_EVENT_RECORDING_LIST_CHANGE_ADD; strncpy(data, tmp + 26, len); } else if (strcmp(tmp, "RECORDING_LIST_CHANGE UPDATE") == 0) { event = CMYTH_EVENT_RECORDING_LIST_CHANGE_UPDATE; /* receive a proginfo structure - do nothing with it (yet?)*/ proginfo = cmyth_proginfo_create(); if (!proginfo) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_proginfo_create() failed\n", __FUNCTION__); goto fail; } consumed = cmyth_rcv_proginfo(conn, &err, proginfo, count); ref_release(proginfo); proginfo = NULL; count -= consumed; } else if (strncmp(tmp, "RECORDING_LIST_CHANGE DELETE", 28) == 0) { event = CMYTH_EVENT_RECORDING_LIST_CHANGE_DELETE; strncpy(data, tmp + 29, len); } else if (strcmp(tmp, "SCHEDULE_CHANGE") == 0) { event = CMYTH_EVENT_SCHEDULE_CHANGE; } else if (strncmp(tmp, "DONE_RECORDING", 14) == 0) { event = CMYTH_EVENT_DONE_RECORDING; } else if (strncmp(tmp, "QUIT_LIVETV", 11) == 0) { event = CMYTH_EVENT_QUIT_LIVETV; } else if (strncmp(tmp, "LIVETV_WATCH", 12) == 0) { event = CMYTH_EVENT_WATCH_LIVETV; strncpy(data, tmp + 13, len); /* Sergio: Added to support the new live tv protocol */ } else if (strncmp(tmp, "LIVETV_CHAIN UPDATE", 19) == 0) { event = CMYTH_EVENT_LIVETV_CHAIN_UPDATE; strncpy(data, tmp + 20, len); } else if (strncmp(tmp, "SIGNAL", 6) == 0) { int dstlen=len; event = CMYTH_EVENT_SIGNAL; /*Get Recorder ID */ strncat(data,"cardid ",7); strncat(data,tmp+7,consumed-12); strncat(data,";",2); /* get slock, signal, seen_pat, matching_pat */ while (count > 0) { /* get signalmonitorvalue name */ consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); count -= consumed; /*strncat(data,tmp,dstlen-2); strncat(data,"=",2); dstlen -= consumed;*/ /* get signalmonitorvalue status */ consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); count -= consumed; strncat(data,tmp,dstlen-2); strncat(data,";",2); dstlen -= consumed; } } else if (strncmp(tmp, "ASK_RECORDING", 13) == 0) { event = CMYTH_EVENT_ASK_RECORDING; if (cmyth_conn_get_protocol_version(conn) < 37) { /* receive 4 string - do nothing with them */ for (i = 0; i < 4; i++) { consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) -1, count); count -= consumed; } } else { /* receive a proginfo structure - do nothing with it (yet?)*/ proginfo = cmyth_proginfo_create(); if (!proginfo) { cmyth_dbg(CMYTH_DBG_ERROR, "%s: cmyth_proginfo_create() failed\n", __FUNCTION__); goto fail; } consumed = cmyth_rcv_proginfo(conn, &err, proginfo, count); ref_release(proginfo); proginfo = NULL; count -= consumed; } } else if (strncmp(tmp, "CLEAR_SETTINGS_CACHE", 20) == 0) { event = CMYTH_EVENT_CLEAR_SETTINGS_CACHE; } else if (strncmp(tmp, "GENERATED_PIXMAP", 16) == 0) { /* capture the file which a pixmap has been generated for */ event = CMYTH_EVENT_GENERATED_PIXMAP; consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); if (strncmp(tmp, "OK", 2) == 0) { consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); strncpy(data, tmp, len); } else { data[0] = 0; } } else if (strncmp(tmp, "SYSTEM_EVENT", 12) == 0) { event = CMYTH_EVENT_SYSTEM_EVENT; strncpy(data, tmp + 13, len); } else if (strncmp(tmp, "UPDATE_FILE_SIZE", 16) == 0) { event = CMYTH_EVENT_UPDATE_FILE_SIZE; strncpy(data, tmp + 17, len); } else { cmyth_dbg(CMYTH_DBG_ERROR, "%s: unknown mythtv BACKEND_MESSAGE '%s'\n", __FUNCTION__, tmp); event = CMYTH_EVENT_UNKNOWN; } while(count > 0) { consumed = cmyth_rcv_string(conn, &err, tmp, sizeof(tmp) - 1, count); count -= consumed; cmyth_dbg(CMYTH_DBG_DEBUG, "%s: leftover data %s\n", __FUNCTION__, tmp); } return event; fail: return CMYTH_EVENT_UNKNOWN; }
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; }
/* * 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_fill(cmyth_conn_t control, cmyth_proginfo_t prog) * * 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; int ret; long long 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; } pthread_mutex_lock(&control->conn_mutex); length = prog->proginfo_Length; if ((ret=fill_command(control, prog, "FILL_PROGRAM_INFO") != 0)) 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; ret = -1; goto out; } ret = 0; out: pthread_mutex_unlock(&control->conn_mutex); return ret; }
/* * 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_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_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 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; }
/* * 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_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_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; }
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; } }
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_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_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_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_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_unlocked(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_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; int64_t c; long r; long long ret; if (file == NULL) return -EINVAL; if ((offset == 0) && (whence == SEEK_CUR)) return file->file_pos; if ((offset == file->file_pos) && (whence == SEEK_SET)) return file->file_pos; while(file->file_pos < file->file_req) { c = file->file_req - file->file_pos; if(c > sizeof(msg)) c = sizeof(msg); if (cmyth_file_get_block(file, msg, (unsigned long)c) < 0) return -1; } pthread_mutex_lock(&mutex); 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 %ld[]:[]SEEK[]:[]%"PRIu64"[]:[]%d[]:[]%"PRIu64, file->file_id, (int64_t)offset, whence, (int64_t)file->file_pos); } else { 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_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; } 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; } 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; }