/* Called by ares library for upload state changes. */ static as_bool up_state_cb (ASUpMan *man, ASUpload *up, ASUploadState state) { Share *share = up->share->udata; Chunk *chunk; Transfer *transfer; #if 1 AS_HEAVY_DBG_2("Upload state for %s: %s", net_ip_str (up->host), as_upload_state_str (up)); #endif switch (state) { case UPLOAD_ACTIVE: transfer = PROTO->upload_start (PROTO, &chunk, upload_to_user (up), share, up->start, up->stop); if (!transfer) { AS_ERR_1 ("Failed to create giFT transfer object for upload to %s", net_ip_str (up->host)); as_upman_cancel (AS->upman, up); as_upman_remove (AS->upman, up); return FALSE; /* Upload was freed. */ } assert (chunk->transfer == transfer); up->udata = chunk; chunk->udata = up; /* Register send progress and throttle callbacks. */ as_upload_set_data_cb (up, up_data_cb); as_upload_set_throttle_cb (up, up_throttle_cb); break; case UPLOAD_COMPLETE: /* This should never happen since we return FALSE from up_data_cb. */ assert (0); /* May make giFT call asp_giftcb_upload_stop. */ send_progress (up); break; case UPLOAD_FAILED: case UPLOAD_CANCELLED: /* Makes giFT call asp_giftcb_upload_stop. */ wrote (up, 0); return FALSE; default: abort (); } return TRUE; }
static gpointer worker_thread(gpointer data) { //TODO consider replacing the main loop with a blocking call on the async queue, //(g_async_queue_pop) waiting for messages. dbg(1, "new worker thread."); g_async_queue_ref(msg_queue); bool done(gpointer _message) { Message* message = _message; message->done(message->sample, message->user_data); send_progress(GINT_TO_POINTER(g_list_length(msg_list))); sample_unref(message->sample); g_free(message); return G_SOURCE_REMOVE; }
void recurse_dir(MediaScan *s, const char *path, int recurse_count) { char *dir, *p; char tmp_full_path[MAX_PATH_STR_LEN]; DIR *dirp; struct dirent *dp; struct dirq *subdirq; // list of subdirs of the current directory struct dirq_entry *parent_entry = NULL; // entry for current dir in s->_dirq char redirect_dir[MAX_PATH_STR_LEN]; if (recurse_count > RECURSE_LIMIT) { LOG_ERROR("Hit recurse limit of %d scanning path %s\n", RECURSE_LIMIT, path); return; } if (path[0] != '/') { // XXX Win32 // Get full path char *buf = (char *)malloc((size_t)MAX_PATH_STR_LEN); if (buf == NULL) { FATAL("Out of memory for directory scan\n"); return; } dir = getcwd(buf, (size_t)MAX_PATH_STR_LEN); strcat(dir, "/"); strcat(dir, path); } else { #ifdef USING_TCMALLOC // strdup will cause tcmalloc to crash on free dir = (char *)malloc((size_t)MAX_PATH_STR_LEN); strcpy(dir, path); #else dir = strdup(path); #endif } // Strip trailing slash if any p = &dir[0]; while (*p != 0) { if (p[1] == 0 && *p == '/') *p = 0; p++; } LOG_INFO("Recursed into %s\n", dir); #if defined(__APPLE__) if (isAlias(dir)) { if (CheckMacAlias(dir, redirect_dir)) { LOG_INFO("Resolving Alias %s to %s\n", dir, redirect_dir); strcpy(dir, redirect_dir); } else { LOG_ERROR("Failure to follow symlink or alias, skipping directory\n"); goto out; } } #elif defined(__linux__) if (isAlias(dir)) { FollowLink(dir, redirect_dir); LOG_INFO("Resolving symlink %s to %s\n", dir, redirect_dir); strcpy(dir, redirect_dir); } #endif if ((dirp = opendir(dir)) == NULL) { LOG_ERROR("Unable to open directory %s: %s\n", dir, strerror(errno)); goto out; } subdirq = malloc(sizeof(struct dirq)); SIMPLEQ_INIT(subdirq); while ((dp = readdir(dirp)) != NULL) { char *name = dp->d_name; // skip all dot files if (name[0] != '.') { // Check if scan should be aborted if (unlikely(s->_want_abort)) break; // XXX some platforms may be missing d_type/DT_DIR if (dp->d_type == DT_DIR) { // Add to list of subdirectories we need to recurse into struct dirq_entry *subdir_entry = malloc(sizeof(struct dirq_entry)); // Construct full path //*tmp_full_path = 0; strcpy(tmp_full_path, dir); strcat(tmp_full_path, "/"); strcat(tmp_full_path, name); if (_should_scan_dir(s, tmp_full_path)) { subdir_entry->dir = strdup(tmp_full_path); SIMPLEQ_INSERT_TAIL(subdirq, subdir_entry, entries); LOG_INFO(" subdir: %s\n", tmp_full_path); } else { LOG_INFO(" skipping subdir: %s\n", tmp_full_path); } } else { enum media_type type = _should_scan(s, name); LOG_INFO("name %s = type %d\n", name, type); if (type) { struct fileq_entry *entry; // Check if this file is a shortcut and if so resolve it #if defined(__APPLE__) if (isAlias(name)) { char full_name[MAX_PATH_STR_LEN]; LOG_INFO("Mac Alias detected\n"); strcpy(full_name, dir); strcat(full_name, "\\"); strcat(full_name, name); parse_lnk(full_name, redirect_dir, MAX_PATH_STR_LEN); if (PathIsDirectory(redirect_dir)) { struct dirq_entry *subdir_entry = malloc(sizeof(struct dirq_entry)); subdir_entry->dir = strdup(redirect_dir); SIMPLEQ_INSERT_TAIL(subdirq, subdir_entry, entries); LOG_INFO(" subdir: %s\n", tmp_full_path); type = 0; } } #elif defined(__linux__) if (isAlias(name)) { char full_name[MAX_PATH_STR_LEN]; printf("Linux Alias detected\n"); strcpy(full_name, dir); strcat(full_name, "\\"); strcat(full_name, name); FollowLink(full_name, redirect_dir); if (PathIsDirectory(redirect_dir)) { struct dirq_entry *subdir_entry = malloc(sizeof(struct dirq_entry)); subdir_entry->dir = strdup(redirect_dir); SIMPLEQ_INSERT_TAIL(subdirq, subdir_entry, entries); LOG_INFO(" subdir: %s\n", tmp_full_path); type = 0; } } #endif if (parent_entry == NULL) { // Add parent directory to list of dirs with files parent_entry = malloc(sizeof(struct dirq_entry)); parent_entry->dir = strdup(dir); parent_entry->files = malloc(sizeof(struct fileq)); SIMPLEQ_INIT(parent_entry->files); SIMPLEQ_INSERT_TAIL((struct dirq *)s->_dirq, parent_entry, entries); } // Add scannable file to this directory list entry = malloc(sizeof(struct fileq_entry)); entry->file = strdup(name); entry->type = type; SIMPLEQ_INSERT_TAIL(parent_entry->files, entry, entries); s->progress->total++; LOG_INFO(" [%5d] file: %s\n", s->progress->total, entry->file); } } } } closedir(dirp); // Send progress update if (s->on_progress && !s->_want_abort) if (progress_update(s->progress, dir)) send_progress(s); // process subdirs while (!SIMPLEQ_EMPTY(subdirq)) { struct dirq_entry *subdir_entry = SIMPLEQ_FIRST(subdirq); SIMPLEQ_REMOVE_HEAD(subdirq, entries); if (!s->_want_abort) recurse_dir(s, subdir_entry->dir, recurse_count); free(subdir_entry); } free(subdirq); out: free(dir); }
// Called by ms_scan either in a thread or synchronously static void *do_scan(void *userdata) { MediaScan *s = ((thread_data_type *)userdata)->s; int i; struct dirq *dir_head = (struct dirq *)s->_dirq; struct dirq_entry *dir_entry = NULL; struct fileq *file_head = NULL; struct fileq_entry *file_entry = NULL; char tmp_full_path[MAX_PATH_STR_LEN]; // Initialize the cache database if (!init_bdb(s)) { MediaScanError *e = error_create("", MS_ERROR_CACHE, "Unable to initialize libmediascan cache"); send_error(s, e); goto out; } if (s->flags & MS_CLEARDB) { reset_bdb(s); } if (s->progress == NULL) { MediaScanError *e = error_create("", MS_ERROR_TYPE_INVALID_PARAMS, "Progress object not created"); send_error(s, e); goto out; } // Build a list of all directories and paths // We do this first so we can present an accurate scan eta later progress_start_phase(s->progress, "Discovering"); for (i = 0; i < s->npaths; i++) { LOG_INFO("Scanning %s\n", s->paths[i]); recurse_dir(s, s->paths[i], 0); } // Scan all files found progress_start_phase(s->progress, "Scanning"); while (!SIMPLEQ_EMPTY(dir_head)) { dir_entry = SIMPLEQ_FIRST(dir_head); file_head = dir_entry->files; while (!SIMPLEQ_EMPTY(file_head)) { // check if the scan has been aborted if (s->_want_abort) { LOG_DEBUG("Aborting scan\n"); goto aborted; } file_entry = SIMPLEQ_FIRST(file_head); // Construct full path strcpy(tmp_full_path, dir_entry->dir); #ifdef WIN32 strcat(tmp_full_path, "\\"); #else strcat(tmp_full_path, "/"); #endif strcat(tmp_full_path, file_entry->file); ms_scan_file(s, tmp_full_path, file_entry->type); // Send progress update if necessary if (s->on_progress) { s->progress->done++; if (progress_update(s->progress, tmp_full_path)) send_progress(s); } SIMPLEQ_REMOVE_HEAD(file_head, entries); free(file_entry->file); free(file_entry); } SIMPLEQ_REMOVE_HEAD(dir_head, entries); free(dir_entry->dir); free(dir_entry->files); free(dir_entry); } // Send final progress callback if (s->on_progress) { progress_update(s->progress, NULL); send_progress(s); } LOG_DEBUG("Finished scanning\n"); out: if (s->on_finish) send_finish(s); aborted: if (s->async) { LOG_MEM("destroy thread_data @ %p\n", userdata); free(userdata); } return NULL; }
virtual void process() { // This happens in the installer thread, so we cannot mess with // anything else. HZIP zip = NULL; try { g_debug( "STARTING INSTALL OF %s", info.source_file.c_str() ); //................................................................. if ( ! verify_and_strip_signatures() ) { throw String( "SIGNATURE CHECK FAILED" ); } //................................................................. // Open the source file to make sure it is ok zip = OpenZip( info.source_file.c_str(), NULL ); if ( ! zip ) { throw String( "FAILED TO OPEN ZIP FILE" ); } ZIPENTRY entry; //................................................................. // Figure out how many items are in the zip file if ( ZR_OK != GetZipItem( zip, -1, &entry ) ) { throw String( "FAILED TO GET ZIP ENTRY COUNT" ); } int entry_count = entry.index; if ( entry_count <= 0 ) { throw String( "ZIP FILE HAS TOO FEW ENTRIES" ); } //................................................................. // Now go through all the items in the zip file, figure out // their total uncompressed size and find the 'app' file. guint64 total_uncompressed_size = 0; String app_file_zip_path; int app_file_zip_index = -1; guint64 app_file_uncompressed_size = 0; for ( int i = 0; i < entry_count; ++i ) { if ( ZR_OK != GetZipItem( zip, i, &entry ) ) { throw String( "FAILED TO GET ZIP ENTRY" ); } total_uncompressed_size += entry.unc_size; // See if this is the app file if ( app_file_zip_path.empty() ) { // THIS IS PLATFORM SPECIFIC if ( ! ( entry.attr & S_IFDIR ) ) { gchar * basename = g_path_get_basename( entry.name ); if ( ! strcmp( basename , "app" ) ) { app_file_zip_path = entry.name; app_file_zip_index = i; app_file_uncompressed_size = entry.unc_size; } g_free( basename ); } } } if ( app_file_zip_path.empty() ) { throw String( "ZIP FILE IS MISSING APP FILE" ); } if ( app_file_uncompressed_size == 0 ) { throw String( "APP FILE UNCOMPRESSED SIZE IS INCORRECT" ); } g_debug( "FOUND APP FILE IN ZIP AT %s", app_file_zip_path.c_str() ); //................................................................. // Uncompress the app file to memory and load its metadata. // We must ensure it is valid and its app_id is the same as the // one passed in. // g_new0 serves to NULL-terminate the contents, which // load_metadata_from_data expects. gchar * app_file_buffer = g_new0( gchar, app_file_uncompressed_size * 2 ); if ( ! app_file_buffer ) { throw String( "FAILED TO ALLOCATE MEMORY TO UNCOMPRESS APP FILE" ); } FreeLater free_later; free_later( app_file_buffer ); if ( ZR_OK != UnzipItem( zip, app_file_zip_index, app_file_buffer, app_file_uncompressed_size * 2 ) ) { throw String( "FAILED TO UNCOMPRESS APP FILE" ); } App::Metadata metadata; if ( ! App::load_metadata_from_data( app_file_buffer, metadata ) ) { throw String( "FAILED TO READ METADATA" ); } if ( metadata.id != info.app_id ) { throw String( "APP ID DOES NOT MATCH" ); } //................................................................. // Figure out where to unzip it to // - should be in the same volume as the app's data directory // // The app may already live in trickplay/apps/<id hash>/source // // We could unzip it to trickplay/apps/installing/<id hash> // The benefit of this is that it would be easy to clean up unfinished // installations by deleting everything in "installing". // // We could unzip it to trickplay/apps/<id hash>/installing // This puts it event closer to its final destination, but we would // have to do more work to clean up. CHOOSING THIS ONE FOR NOW gchar * unzip_path = g_build_filename( info.app_directory.c_str(), "installing", NULL ); free_later( unzip_path ); //................................................................. // If our destination directory already exists, it is probably // from a failed attempt to install this app. We need to get rid of it. if ( g_file_test( unzip_path, G_FILE_TEST_EXISTS ) ) { g_debug( "DELETING OLD INSTALL DIRECTORY %s", unzip_path ); if ( ! recursive_delete_path( unzip_path ) ) { throw String( "FAILED TO DELETE OLD INSTALL DIRECTORY" ); } } if ( 0 != g_mkdir_with_parents( unzip_path, 0700 ) ) { throw String( "FAILED TO CREATE INSTALL DIRECTORY" ); } //................................................................. // TODO: We should now check for free space - and make sure we have at // least total_uncompressed_size available. //................................................................. // OK, everything seems to be in order. // We get the dirname of the path to the app file in the zip. // So, for example, inside the zip, the app file might be in // "foor/bar/app". We have to take all the files in the zip that // are in "foo/bar" and unzip them to our real destination. gchar * app_file_zip_dirname = g_path_get_dirname( app_file_zip_path.c_str() ); g_assert(app_file_zip_dirname); free_later( app_file_zip_dirname ); guint app_file_zip_dirname_length = strlen( app_file_zip_dirname ); // If there is no dirname, the above gets set to "." bool no_zip_root = ! strcmp( app_file_zip_dirname, "." ); // Now, it is time to unzip g_debug( "UNZIPPING TO %s", unzip_path ); guint64 total_processed = 0; Util::GTimer progress_timer; #ifndef TP_PRODUCTION static float slow = -1; if ( slow == -1 ) { if ( const char * e = g_getenv( "TP_INSTALL_DELAY" ) ) { slow = atof( e ); } else { slow = 0; } } #endif for ( int i = 0; i < entry_count; ++i ) { #ifndef TP_PRODUCTION if ( slow ) { usleep( slow * G_USEC_PER_SEC ); } #endif if ( ZR_OK != GetZipItem( zip, i, &entry ) ) { throw String( "FAILED TO GET ZIP ENTRY" ); } gchar * destination_file_name = NULL; if ( no_zip_root ) { destination_file_name = g_build_filename( unzip_path, entry.name, NULL ); } else if ( g_str_has_prefix( entry.name, app_file_zip_dirname ) ) { destination_file_name = g_build_filename( unzip_path, entry.name + app_file_zip_dirname_length, NULL ); } if ( ! destination_file_name ) { g_debug( " SKIPPING %s", entry.name ); } else { free_later( destination_file_name ); g_debug( " UNZIPPING %s", entry.name ); if ( ZR_OK != UnzipItem( zip, i, destination_file_name ) ) { throw String( "FAILED TO UNZIP" ); } } // Report progress total_processed += entry.unc_size; if ( progress_timer.elapsed() >= 1 ) { progress_timer.reset(); info.status = Installer::Info::INSTALLING; info.percent_installed = gdouble( total_processed ) / gdouble( total_uncompressed_size ) * 100.0; send_progress(); } } //................................................................. // At this point, the app should be uncompressed to the "installing" // directory and ready to go. //................................................................. // Finally, under the right conditions, we delete the existing install // of the app and move the "installing" directory over it. info.moved = false; gchar * source_path = g_build_filename( info.app_directory.c_str(), "source", NULL ); free_later( source_path ); bool source_exists = g_file_test( source_path, G_FILE_TEST_EXISTS ); // If the source directory exists and the app is locked, we can // delete the source directory if ( source_exists && info.locked ) { if ( ! recursive_delete_path( source_path ) ) { throw String( "FAILED TO DELETE PREVIOUS APP SOURCE" ); } source_exists = false; } // If the source directory does not already exist, or we deleted in the // previous step, we can rename the install directory. if ( ! source_exists ) { if ( 0 != g_rename( unzip_path, source_path ) ) { throw String( "FAILED TO RENAME INSTALL DIRECTORY TO SOURCE DIRECTORY" ); } info.moved = true; } // Once this is done, the caller needs to call 'complete_install'. This will // move the app to its final resting place (if necessary) and also add its // entry to the system database. g_debug( "FINISHED INSTALL OF %s TO %s", info.app_id.c_str(), info.moved ? source_path : unzip_path ); info.status = Installer::Info::FINISHED; info.install_directory = unzip_path; info.app_directory = source_path; send_progress(); // Caller is also reponsible for getting rid of the original zip file. } catch( const String & e ) { g_warning( "FAILED TO INSTALL %s FROM %s : %s", info.app_id.c_str(), info.source_file.c_str(), e.c_str() ); info.status = Installer::Info::FAILED; send_progress( ); } // Close the zip file if ( zip ) { CloseZip( zip ); } // Always return true - to keep the thread running }