// The contents of this file are subject to the BOINC Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://boinc.berkeley.edu/license_1.0.txt // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is the Berkeley Open Infrastructure for Network Computing. // // The Initial Developer of the Original Code is the SETI@home project. // Portions created by the SETI@home project are Copyright (C) 2002 // University of California at Berkeley. All Rights Reserved. // // Contributor(s): // #include "cpp.h" #ifdef _WIN32 #include "stdafx.h" #endif #ifndef _WIN32 #include #include #endif #include "error_numbers.h" #include "file_names.h" #include "filesys.h" #include "client_msgs.h" #include "log_flags.h" #include "parse.h" #include "util.h" #include "client_state.h" #include "pers_file_xfer.h" #include "client_types.h" PROJECT::PROJECT() { init(); } void PROJECT::init() { strcpy(master_url, ""); strcpy(authenticator, ""); project_specific_prefs = ""; resource_share = 100; strcpy(project_name, ""); strcpy(user_name, ""); strcpy(team_name, ""); user_total_credit = 0; user_expavg_credit = 0; user_create_time = 0; rpc_seqno = 0; hostid = 0; host_total_credit = 0; host_expavg_credit = 0; host_create_time = 0; exp_avg_cpu = 0; exp_avg_mod_time = 0; strcpy(code_sign_key, ""); nrpc_failures = 0; min_rpc_time = 0; min_report_min_rpc_time = 0; master_fetch_failures = 0; resource_debt = 0; debt_order = 0; master_url_fetch_pending = false; sched_rpc_pending = false; tentative = false; anonymous_platform = false; } PROJECT::~PROJECT() { } // write account_*.xml file. // NOTE: this is called only when // 1) attach to a project, and // 2) after a scheduler RPC // So in either case PROJECT.project_prefs // (which normally is undefined) is valid // int PROJECT::write_account_file() { char path[256]; FILE* f; int retval; get_account_filename(master_url, path); f = boinc_fopen(TEMP_FILE_NAME, "w"); if (!f) return ERR_FOPEN; #ifndef _WIN32 chmod(TEMP_FILE_NAME, 0600); #endif fprintf(f, "\n" " %s\n" " %s\n", master_url, authenticator ); // put project name in account file for informational purposes only // (client state file is authoritative) // if (strlen(project_name)) { fprintf(f, " %s\n", project_name); } if (tentative) { fprintf(f, " \n"); } fprintf(f, "\n%s\n", project_prefs.c_str() ); fprintf(f, "\n"); fclose(f); retval = boinc_rename(TEMP_FILE_NAME, path); if (retval) return ERR_RENAME; return 0; } int PROJECT::parse_account_file() { char path[256]; int retval; FILE* f; get_account_filename(master_url, path); f = fopen(path, "r"); if (!f) return ERR_FOPEN; retval = parse_account(f); fclose(f); return retval; } // parse user's project preferences, generating // FILE_REF and FILE_INFO objects for each element. // int PROJECT::parse_preferences_for_user_files() { char* p, *q, *q2; char buf[1024]; string timestamp, open_name, url, filename; FILE_INFO* fip; FILE_REF fr; STRING256 url_str; char prefs_buf[MAX_BLOB_LEN]; strcpy(prefs_buf, project_specific_prefs.c_str()); p = prefs_buf; user_files.clear(); while (1) { q = strstr(p, ""); if (!q) break; q2 = strstr(q, ""); if (!q2) break; *q2 = 0; strcpy(buf, q); if (!parse_str(buf, "", timestamp)) break; if (!parse_str(buf, "", open_name)) break; if (!parse_str(buf, "", url)) break; strcpy(url_str.text, url.c_str()); filename = open_name + "_" + timestamp; fip = gstate.lookup_file_info(this, filename.c_str()); if (!fip) { fip = new FILE_INFO; fip->urls.push_back(url_str); strcpy(fip->name, filename.c_str()); fip->project = this; fip->is_user_file = true; gstate.file_infos.push_back(fip); } fr.file_info = fip; strcpy(fr.open_name, open_name.c_str()); user_files.push_back(fr); p = q2+strlen(""); } return 0; } // parse account_*.xml file; fill in files of // project_specific_prefs // int PROJECT::parse_account(FILE* in) { char buf[256], venue[256]; char temp[MAX_BLOB_LEN]; int retval; bool got_venue_prefs = false; // Assume master_url_fetch_pending, sched_rpc_pending are // true until we read client_state.xml // master_url_fetch_pending = true; sched_rpc_pending = true; strcpy(master_url, ""); strcpy(authenticator, ""); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) { if (strlen(gstate.host_venue)) { if (!got_venue_prefs) { msg_printf(this, MSG_INFO, "Project prefs: no separate prefs for %s; using your defaults", gstate.host_venue ); } } else { msg_printf(this, MSG_INFO, "Project prefs: using your defaults"); } return 0; } else if (match_tag(buf, "", temp, sizeof(temp)); } continue; } else if (parse_str(buf, "", master_url, sizeof(master_url))) { canonicalize_master_url(master_url); continue; } else if (parse_str(buf, "", authenticator, sizeof(authenticator))) continue; else if (parse_double(buf, "", resource_share)) continue; else if (parse_str(buf, "", project_name, sizeof(project_name))) continue; else if (match_tag(buf, "")) { tentative = true; continue; } else if (match_tag(buf, "")) { retval = copy_element_contents( in, "", project_specific_prefs ); if (retval) return ERR_XML_PARSE; continue; } else msg_printf(NULL, MSG_ERROR, "PROJECT::parse_account(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } // parse project fields from client_state.xml // int PROJECT::parse_state(FILE* in) { char buf[256]; STRING256 sched_url; string str1, str2; strcpy(project_name, ""); strcpy(user_name, ""); strcpy(team_name, ""); resource_share = 100; exp_avg_cpu = 0; exp_avg_mod_time = 0; min_rpc_time = 0; min_report_min_rpc_time = 0; nrpc_failures = 0; master_url_fetch_pending = false; sched_rpc_pending = false; scheduler_urls.clear(); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", sched_url.text, sizeof(sched_url.text))) { scheduler_urls.push_back(sched_url); continue; } else if (parse_str(buf, "", master_url, sizeof(master_url))) continue; else if (parse_str(buf, "", project_name, sizeof(project_name))) continue; else if (parse_str(buf, "", user_name, sizeof(user_name))) continue; else if (parse_str(buf, "", team_name, sizeof(team_name))) continue; else if (parse_double(buf, "", user_total_credit)) continue; else if (parse_double(buf, "", user_expavg_credit)) continue; else if (parse_int(buf, "", (int &)user_create_time)) continue; else if (parse_int(buf, "", rpc_seqno)) continue; else if (parse_int(buf, "", hostid)) continue; else if (parse_double(buf, "", host_total_credit)) continue; else if (parse_double(buf, "", host_expavg_credit)) continue; else if (parse_int(buf, "", (int &)host_create_time)) continue; else if (parse_double(buf, "", exp_avg_cpu)) continue; else if (parse_double(buf, "", exp_avg_mod_time)) continue; else if (match_tag(buf, "")) { copy_element_contents( in, "", code_sign_key, sizeof(code_sign_key) ); } else if (parse_int(buf, "", nrpc_failures)) continue; else if (parse_int(buf, "", master_fetch_failures)) continue; else if (parse_int(buf, "", (int&)min_rpc_time)) continue; else if (match_tag(buf, "")) master_url_fetch_pending = true; else if (match_tag(buf, "")) sched_rpc_pending = true; else msg_printf(NULL, MSG_ERROR, "PROJECT::parse_state(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } // Write the project information to client state file // int PROJECT::write_state(FILE* out) { unsigned int i; string u1, u2, t1, t2; fprintf(out, "\n" ); for (i=0; i%s\n", scheduler_urls[i].text ); } u1 = user_name; xml_escape(u1, u2); t1 = team_name; xml_escape(t1, t2); fprintf(out, " %s\n" " %s\n" " %s\n" " %s\n" " %f\n" " %f\n" " %d\n" " %d\n" " %d\n" " %f\n" " %f\n" " %d\n" " %f\n" " %f\n" " %d\n" " %d\n" " %d\n" "%s%s", master_url, project_name, u2.c_str(), t2.c_str(), user_total_credit, user_expavg_credit, user_create_time, rpc_seqno, hostid, host_total_credit, host_expavg_credit, host_create_time, exp_avg_cpu, exp_avg_mod_time, nrpc_failures, master_fetch_failures, (int)min_rpc_time, master_url_fetch_pending?" \n":"", sched_rpc_pending?" \n":"" ); if (strlen(code_sign_key)) { fprintf(out, " \n%s\n", code_sign_key ); } fprintf(out, "\n" ); return 0; } // copy fields from "p" into "this" that are stored in client_state.xml // void PROJECT::copy_state_fields(PROJECT& p) { scheduler_urls = p.scheduler_urls; safe_strcpy(project_name, p.project_name); safe_strcpy(user_name, p.user_name); safe_strcpy(team_name, p.team_name); user_total_credit = p.user_total_credit; user_expavg_credit = p.user_expavg_credit; user_create_time = p.user_create_time; rpc_seqno = p.rpc_seqno; hostid = p.hostid; host_total_credit = p.host_total_credit; host_expavg_credit = p.host_expavg_credit; host_create_time = p.host_create_time; exp_avg_cpu = p.exp_avg_cpu; exp_avg_mod_time = p.exp_avg_mod_time; nrpc_failures = p.nrpc_failures; master_fetch_failures = p.master_fetch_failures; min_rpc_time = p.min_rpc_time; master_url_fetch_pending = p.master_url_fetch_pending; sched_rpc_pending = p.sched_rpc_pending; safe_strcpy(code_sign_key, p.code_sign_key); } char* PROJECT::get_project_name() { if (strlen(project_name)) { return project_name; } else { return master_url; } } int APP::parse(FILE* in) { char buf[256]; strcpy(name, ""); project = NULL; while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", name, sizeof(name))) continue; else msg_printf(NULL, MSG_ERROR, "APP::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } int APP::write(FILE* out) { fprintf(out, "\n" " %s\n" "\n", name ); return 0; } FILE_INFO::FILE_INFO() { strcpy(name, ""); strcpy(md5_cksum, ""); max_nbytes = 0; nbytes = 0; upload_offset = -1; generated_locally = false; status = FILE_NOT_PRESENT; executable = false; uploaded = false; upload_when_present = false; sticky = false; signature_required = false; is_user_file = false; pers_file_xfer = NULL; result = NULL; project = NULL; urls.clear(); start_url = -1; current_url = -1; strcpy(signed_xml, ""); strcpy(xml_signature, ""); strcpy(file_signature, ""); } FILE_INFO::~FILE_INFO() { if (pers_file_xfer) { msg_printf(NULL, MSG_ERROR, "FILE_INFO::~FILE_INFO(): removing FILE_INFO when a pers_file_xfer still points to me\n"); pers_file_xfer->fip = NULL; } } // Set the appropriate permissions depending on whether // it's an executable file // This doesn't seem to exist in Windows // int FILE_INFO::set_permissions() { #ifdef _WIN32 return 0; #else int retval; char pathname[256]; get_pathname(this, pathname); if (executable) { retval = chmod(pathname, S_IEXEC|S_IREAD|S_IWRITE); } else { retval = chmod(pathname, S_IREAD|S_IWRITE); } return retval; #endif } // see if a file markes as present actually IS present // and have the right size. // If not, mark it as not present // bool FILE_INFO::verify_existing_file() { int retval; double size; char path[256]; get_pathname(this, path); retval = file_size(path, size); if (retval) { status = FILE_NOT_PRESENT; return false; } if (!log_flags.dont_check_file_sizes && size!=nbytes) { status = FILE_NOT_PRESENT; return false; } return true; } // If from server, make an exact copy of everything // except the start/end tags and the element. // int FILE_INFO::parse(FILE* in, bool from_server) { char buf[256], buf2[1024]; STRING256 url; PERS_FILE_XFER *pfxp; int retval; while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (match_tag(buf, "")) { copy_element_contents( in, "", xml_signature, sizeof(xml_signature) ); continue; } if (from_server) { strcat(signed_xml, buf); } if (parse_str(buf, "", name, sizeof(name))) continue; else if (parse_str(buf, "", url.text, sizeof(url.text))) { urls.push_back(url); continue; } else if (match_tag(buf, "")) { copy_element_contents( in, "", file_signature, sizeof(file_signature) ); continue; } else if (parse_str(buf, "", md5_cksum, sizeof(md5_cksum))) continue; else if (parse_double(buf, "", nbytes)) continue; else if (parse_double(buf, "", max_nbytes)) continue; else if (match_tag(buf, "")) generated_locally = true; else if (parse_int(buf, "", status)) continue; else if (match_tag(buf, "")) executable = true; else if (match_tag(buf, "")) uploaded = true; else if (match_tag(buf, "")) upload_when_present = true; else if (match_tag(buf, "")) sticky = true; else if (match_tag(buf, "")) signature_required = true; else if (match_tag(buf, "")) { pfxp = new PERS_FILE_XFER; retval = pfxp->parse(in); if (!retval) { pers_file_xfer = pfxp; } else { delete pfxp; } } else if (!from_server && match_tag(buf, "")) { copy_element_contents( in, "", signed_xml, sizeof(signed_xml) ); continue; } else if (match_tag(buf, "")) { while (fgets(buf, 256, in)) { if (match_tag(buf, "")) break; } continue; } else if (match_tag(buf, "")) { copy_element_contents(in, "", buf2, sizeof(buf2)); error_msg = buf2; } else { msg_printf(NULL, MSG_ERROR, "FILE_INFO::parse(): unrecognized: %s\n", buf); } } return ERR_XML_PARSE; } int FILE_INFO::write(FILE* out, bool to_server) { unsigned int i; int retval; fprintf(out, "\n" " %s\n" " %f\n" " %f\n", name, nbytes, max_nbytes ); if (strlen(md5_cksum)) { fprintf(out, " %s\n", md5_cksum ); } if (!to_server) { if (generated_locally) fprintf(out, " \n"); fprintf(out, " %d\n", status); if (executable) fprintf(out, " \n"); if (uploaded) fprintf(out, " \n"); if (upload_when_present) fprintf(out, " \n"); if (sticky) fprintf(out, " \n"); if (signature_required) fprintf(out, " \n"); if (file_signature) fprintf(out," \n%s\n", file_signature); } for (i=0; i%s\n", urls[i].text); } if (!to_server && pers_file_xfer) { retval = pers_file_xfer->write(out); if (retval) return retval; } if (!to_server) { if (strlen(signed_xml) && strlen(xml_signature)) { fprintf(out, " \n%s \n", signed_xml); } if (strlen(xml_signature)) { fprintf(out, " \n%s \n", xml_signature); } } if (!error_msg.empty()) { fprintf(out, " \n%s\n", error_msg.c_str()); } fprintf(out, "\n"); return 0; } // delete physical underlying file associated with FILE_INFO // int FILE_INFO::delete_file() { char path[256]; get_pathname(this, path); int retval = boinc_delete_file(path); if (retval && status != FILE_NOT_PRESENT) { msg_printf(project, MSG_ERROR, "Couldn't delete file %s\n", path); } status = FILE_NOT_PRESENT; return retval; } // get the currently selected url to download/upload file, or // select one if none is chosen yet // char* FILE_INFO::get_url() { double temp; if (current_url < 0) { temp = rand(); temp *= urls.size(); temp /= RAND_MAX; current_url = (int)temp; start_url = current_url; } return urls[current_url].text; } // Returns true if the file had an unrecoverable error // (couldn't download, RSA/MD5 check failed, etc) // bool FILE_INFO::had_failure(int& failnum) { if (status != FILE_NOT_PRESENT && status != FILE_PRESENT) { failnum = status; return true; } return false; } // Parse XML based app_version information, usually from client_state.xml // int APP_VERSION::parse(FILE* in) { char buf[256]; FILE_REF file_ref; strcpy(app_name, ""); version_num = 0; app = NULL; project = NULL; while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", app_name, sizeof(app_name))) continue; else if (match_tag(buf, "")) { file_ref.parse(in); app_files.push_back(file_ref); continue; } else if (parse_int(buf, "", version_num)) continue; else msg_printf(NULL, MSG_ERROR, "APP_VERSION::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } int APP_VERSION::write(FILE* out) { unsigned int i; int retval; fprintf(out, "\n" " %s\n" " %d\n", app_name, version_num ); for (i=0; i\n" ); return 0; } int FILE_REF::parse(FILE* in) { char buf[256]; strcpy(file_name, ""); strcpy(open_name, ""); fd = -1; main_program = false; copy_file = false; while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", file_name, sizeof(file_name))) continue; else if (parse_str(buf, "", open_name, sizeof(open_name))) continue; else if (parse_int(buf, "", fd)) continue; else if (match_tag(buf, "")) main_program = true; else if (match_tag(buf, "")) copy_file = true; else msg_printf(NULL, MSG_ERROR, "FILE_REF::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } int FILE_REF::write(FILE* out) { fprintf(out, " \n" " %s\n", file_name ); if (strlen(open_name)) { fprintf(out, " %s\n", open_name); } if (fd >= 0) { fprintf(out, " %d\n", fd); } if (main_program) { fprintf(out, " \n"); } if (copy_file) { fprintf(out, " \n"); } fprintf(out, " \n"); return 0; } int WORKUNIT::parse(FILE* in) { char buf[256]; FILE_REF file_ref; strcpy(name, ""); strcpy(app_name, ""); version_num = 0; strcpy(command_line, ""); strcpy(env_vars, ""); app = NULL; project = NULL; // Default these to very large values (1 week on a 1 cobblestone machine) // so we don't keep asking the server for more work rsc_fpops_est = 1e9*SECONDS_PER_DAY*7; rsc_fpops_bound = 4e9*SECONDS_PER_DAY*7; rsc_memory_bound = 1e8; rsc_disk_bound = 1e9; while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", name, sizeof(name))) continue; else if (parse_str(buf, "", app_name, sizeof(app_name))) continue; else if (parse_int(buf, "", version_num)) continue; else if (parse_str(buf, "", command_line, sizeof(command_line))) continue; else if (parse_str(buf, "", env_vars, sizeof(env_vars))) continue; else if (parse_double(buf, "", rsc_fpops_est)) continue; else if (parse_double(buf, "", rsc_fpops_bound)) continue; else if (parse_double(buf, "", rsc_memory_bound)) continue; else if (parse_double(buf, "", rsc_disk_bound)) continue; else if (match_tag(buf, "")) { file_ref.parse(in); input_files.push_back(file_ref); continue; } else msg_printf(NULL, MSG_ERROR, "WORKUNIT::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } int WORKUNIT::write(FILE* out) { unsigned int i; fprintf(out, "\n" " %s\n" " %s\n" " %d\n" " %s\n" " %s\n" " %f\n" " %f\n" " %f\n" " %f\n", name, app_name, version_num, command_line, env_vars, rsc_fpops_est, rsc_fpops_bound, rsc_memory_bound, rsc_disk_bound ); for (i=0; i\n"); return 0; } bool WORKUNIT::had_failure(int& failnum) { unsigned int i; for (i=0;ihad_failure(failnum)) { return true; } } return false; } void WORKUNIT::get_file_errors(string& str) { int x; unsigned int i; FILE_INFO* fip; str = "couldn't get input files:\n"; for (i=0;ihad_failure(x)) { str = str + fip->name + ": " + fip->error_msg + "\n"; } } } int RESULT::parse_ack(FILE* in) { char buf[256]; strcpy(name, ""); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; else if (parse_str(buf, "", name, sizeof(name))) continue; else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } void RESULT::clear() { strcpy(name, ""); strcpy(wu_name, ""); report_deadline = 0; output_files.clear(); is_active = false; state = RESULT_NEW; ready_to_report = false; got_server_ack = false; final_cpu_time = 0; exit_status = 0; active_task_state = 0; signal = 0; stderr_out = ""; app = NULL; wup = NULL; project = NULL; } // parse a element from scheduling server. // int RESULT::parse_server(FILE* in) { char buf[256]; FILE_REF file_ref; clear(); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) return 0; if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_str(buf, "", wu_name, sizeof(wu_name))) continue; if (parse_int(buf, "", report_deadline)) continue; if (match_tag(buf, "")) { file_ref.parse(in); output_files.push_back(file_ref); continue; } else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } // parse a element from state file // int RESULT::parse_state(FILE* in) { char buf[256]; FILE_REF file_ref; clear(); while (fgets(buf, 256, in)) { if (match_tag(buf, "")) { // restore some invariants in case of bad state file // if (got_server_ack || ready_to_report) { state = RESULT_FILES_UPLOADED; } return 0; } if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_str(buf, "", wu_name, sizeof(wu_name))) continue; if (parse_int(buf, "", report_deadline)) continue; if (match_tag(buf, "")) { file_ref.parse(in); output_files.push_back(file_ref); continue; } else if (parse_double(buf, "", final_cpu_time)) continue; else if (parse_int(buf, "", exit_status)) continue; else if (match_tag(buf, "")) got_server_ack = true; else if (match_tag(buf, "")) ready_to_report = true; else if (parse_int(buf, "", state)) continue; else if (match_tag(buf, "")) { while (fgets(buf, 256, in)) { if (match_tag(buf, "")) break; stderr_out.append(buf); } continue; } else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf); } return ERR_XML_PARSE; } int RESULT::write(FILE* out, bool to_server) { unsigned int i; FILE_INFO* fip; int n, retval; fprintf(out, "\n" " %s\n" " %f\n" " %d\n" " %d\n" " %d\n", name, final_cpu_time, exit_status, wup->version_num, state ); n = stderr_out.length(); if (n) { fprintf(out, "\n"); if (to_server) { fprintf(out, "%d.%.2d\n", gstate.core_client_major_version, gstate.core_client_minor_version ); } fprintf(out, stderr_out.c_str()); if (stderr_out[n-1] != '\n') fprintf(out, "\n"); fprintf(out, "\n"); } if (to_server) { for (i=0; iuploaded) { retval = fip->write(out, true); if (retval) return retval; } } } else { if (got_server_ack) fprintf(out, " \n"); if (ready_to_report) fprintf(out, " \n"); fprintf(out, " %s\n" " %d\n", wu_name, report_deadline ); for (i=0; i\n"); return 0; } // this is called after the result state is RESULT_COMPUTE_DONE. // Returns true if the result's output files are all either // successfully uploaded or have unrecoverable errors // bool RESULT::is_upload_done() { unsigned int i; FILE_INFO* fip; int retval; for (i=0; iupload_when_present) { if (fip->had_failure(retval)) continue; if (!fip->uploaded) { return false; } } } return true; } void RESULT::get_app_version_string(string& str) { char buf[256]; sprintf(buf, " %.2f", wup->version_num/100.); str = app->name + string(buf); }