| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <time.h> |
| #include <sys/file.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/reboot.h> |
| #include <sys/wait.h> |
| #include <sys/time.h> |
| #include <linux/capability.h> |
| #include <linux/prctl.h> |
| #include <linux/reboot.h> |
| #include <fcntl.h> |
| #include <pthread.h> |
| #include <libprop2uci/properties.h> |
| #include <paths_defs.h> |
| #include <mtd/mtd-user.h> |
| #include <sys/ioctl.h> |
| #include <dirent.h> |
| #include <sys/sysinfo.h> |
| #include <cutils/log.h> |
| #include <ml_utils.h> |
| |
| /** |
| * ************************************************************ |
| * mll_utils.c |
| * ************************************************************ |
| */ |
| |
| #define FOTA_FLAG_WRITE_COMMAND "busybox dd if=%s of=/dev/mtdblock%d bs=%d count=%d seek=%d conv=fsync" |
| #define FOTA_FLAG_ERASE_COMMAND "busybox flash_eraseall /dev/mtd%d" |
| #define FOTA_FLAG_TMP_FILENAME TEMP_DIR "FOTA_FLAG.bin" |
| #define FOTA_PARTITION_NAME "misc" |
| #define BLOCK_SIZE_MTD (1024) |
| #define FOTA_FLAG_BLOCK_COUNT (1) |
| #define FOTA_FLAG_OFFSET (0x0) |
| #define FOTA_FLAG_COMMAND_MAX_LEN (256) |
| |
| #define MTD_PROC_FILENAME "/proc/mtd" |
| |
| struct fota_info { |
| unsigned int fota_id; |
| unsigned int fota_status; |
| unsigned int num_retries; |
| unsigned int fota_mode; |
| }; |
| |
| |
| int ml_reboot_service(int nosync, int poweroff, void *opt) |
| { |
| int pid; |
| int ret; |
| |
| if (!nosync) { |
| sync(); |
| /* Attempt to unmount the SD card first. |
| * No need to bother checking for errors. |
| */ |
| pid = fork(); |
| if (pid == 0) { |
| /* ask vdc to unmount it */ |
| execl("/system/bin/vdc", "/system/bin/vdc", "volume", "unmount", |
| getenv("EXTERNAL_STORAGE"), "force", NULL); |
| } else if (pid > 0) { |
| /* wait until vdc succeeds or fails */ |
| waitpid(pid, &ret, 0); |
| } |
| } |
| |
| if (poweroff) |
| //ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_POWER_OFF, NULL); |
| ret = reboot(RB_AUTOBOOT); |
| else if (opt) |
| //ret = __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, opt); |
| ret = reboot(RB_AUTOBOOT); |
| else |
| ret = reboot(RB_AUTOBOOT); |
| |
| if (ret < 0) { |
| fprintf(stderr, "%s: reboot failed: %s\n", __FUNCTION__, strerror(errno)); |
| } |
| return ret; |
| } |
| |
| static int ml_flashEraseBlockMTD(int mtd_block_num) |
| { |
| int retVal = 0; |
| int len; |
| char shell_cmd[FOTA_FLAG_COMMAND_MAX_LEN]; |
| |
| len = snprintf(shell_cmd, FOTA_FLAG_COMMAND_MAX_LEN, |
| FOTA_FLAG_ERASE_COMMAND, |
| mtd_block_num); |
| if (len > FOTA_FLAG_COMMAND_MAX_LEN) |
| return -1; |
| retVal = ml_system(10, shell_cmd); |
| sync(); |
| return retVal; |
| } |
| |
| struct fota_info info; |
| |
| int ml_flash_flag_write(int flash_flag) |
| { |
| int len; |
| char shell_cmd[FOTA_FLAG_COMMAND_MAX_LEN]; |
| FILE *file; |
| int retVal = 0; |
| int mtd_block_num; |
| |
| /*fill fota info and write to temp file*/ |
| // FOTA ID is 0x464F5441- here written the LE |
| info.fota_id = 0x41544f46; |
| info.fota_status = 0x00000000 | ((int)flash_flag); |
| info.num_retries = 0; |
| |
| file = fopen(FOTA_FLAG_TMP_FILENAME, "w"); |
| if (!file) |
| return -2; |
| retVal = fwrite(&info, sizeof(struct fota_info), 1, file); |
| if (retVal == 0) |
| return -2; |
| retVal = fclose(file); |
| if (retVal != 0) |
| return -2; |
| sync(); |
| |
| mtd_block_num = ml_get_mtd_block_num(FOTA_PARTITION_NAME); |
| if(mtd_block_num < 0) |
| return -1; |
| |
| if(ml_flashEraseBlockMTD(mtd_block_num) < 0) |
| return -1; |
| |
| /* write fota flag to flash using DD command*/ len = snprintf( |
| shell_cmd, FOTA_FLAG_COMMAND_MAX_LEN, FOTA_FLAG_WRITE_COMMAND, |
| FOTA_FLAG_TMP_FILENAME, mtd_block_num, BLOCK_SIZE_MTD, |
| FOTA_FLAG_BLOCK_COUNT, FOTA_FLAG_OFFSET); |
| |
| if (len > FOTA_FLAG_COMMAND_MAX_LEN) |
| return -3; |
| |
| /* initiate command */ |
| retVal = ml_system(10, shell_cmd); |
| sync(); |
| return retVal; |
| } |
| |
| int ml_get_mtd_block_num (char *mtd_name) |
| { |
| char buf[2048]; |
| const char *bufp; |
| int fd; |
| int ret = -1; |
| ssize_t nbytes; |
| |
| if (mtd_name == NULL || *mtd_name == 0 ){ |
| return -1; |
| } |
| |
| /* Open and read the file contents. |
| */ |
| fd = open(MTD_PROC_FILENAME, O_RDONLY); |
| if (fd < 0) { |
| goto exit; |
| } |
| nbytes = read(fd, buf, sizeof(buf) - 1); |
| close(fd); |
| if (nbytes < 0) { |
| goto exit; |
| } |
| buf[nbytes] = '\0'; |
| |
| /* Parse the contents of the file, which looks like: |
| * |
| * # cat /proc/mtd |
| * dev: size erasesize name |
| * mtd0: 00080000 00020000 "bootloader" |
| * mtd1: 00400000 00020000 "mfg_and_gsm" |
| * mtd2: 00400000 00020000 "0000000c" |
| * mtd3: 00200000 00020000 "0000000d" |
| * mtd4: 04000000 00020000 "system" |
| * mtd5: 03280000 00020000 "userdata" |
| */ |
| bufp = buf; |
| while (nbytes > 0) { |
| int mtdnum, mtdsize, mtderasesize; |
| int matches; |
| char mtdname[64]; |
| mtdname[0] = '\0'; |
| mtdnum = -1; |
| |
| matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]", |
| &mtdnum, &mtdsize, &mtderasesize, mtdname); |
| /* This will fail on the first line, which just contains |
| * column headers. |
| */ |
| if (matches == 4) { |
| if(strcmp(mtdname,mtd_name) == 0 ) |
| { |
| /* Found partition name, return partition number*/ |
| ret = mtdnum; |
| goto exit; |
| } |
| } |
| /* Eat the line. |
| */ |
| while (nbytes > 0 && *bufp != '\n') { |
| bufp++; |
| nbytes--; |
| } |
| if (nbytes > 0) { |
| bufp++; |
| nbytes--; |
| } |
| } |
| exit: |
| if(fd >=0 ) |
| close(fd); |
| return ret; |
| } |
| |
| |
| int ml_nand(int argc, char *argv[]) |
| { |
| |
| int ret = 0; |
| if (argc < 2) { |
| printf("please enter 2 params\n"); |
| return -1; |
| } |
| info.fota_mode = atoi(argv[1]); |
| ret = ml_flash_flag_write(atoi(argv[0])); |
| |
| return ret; |
| } |
| |
| /** |
| * ************************************************************ |
| * mll_phone.c |
| * ************************************************************ |
| */ |
| #include <paths_defs.h> |
| #include <ml_utils.h> |
| |
| static struct ml_imei g_imei; |
| static int g_imei_good; |
| static struct ml_vers g_vers; |
| static int g_vers_good; |
| static struct ml_bld_vers g_bld_vers; |
| static int g_bld_vers_good; |
| static struct ml_cp_ver g_cp_ver; |
| static int g_cp_ver_good; |
| |
| char *ml_get_bld_vers(struct ml_bld_vers *m); |
| |
| /*Mutex to synchronize getting unique id*/ |
| static pthread_mutex_t hawk_unique_id_mutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| /** |
| * ml_set_long_property |
| * Sets value of the int number into android properties |
| * |
| * @param name - property name, value - int value to be saved |
| * into property |
| */ |
| void ml_set_property(const char *name, int value){ |
| char tmp[255]; |
| sprintf(tmp, "%d", value); |
| property_set(name, tmp); |
| } |
| |
| /** |
| * ml_get_long_property |
| * This fucntion return android property as int number |
| * |
| * @param name - property name |
| * @return int numer stored in property, or 0 incase property |
| * doesn't exist |
| */ |
| int ml_get_property(const char *name){ |
| int value = 0; |
| char tmp[255]; |
| property_get(name, tmp, "0"); |
| value = atoi(tmp); |
| return value; |
| } |
| |
| /** |
| * ml_set_long_property |
| * Sets value of the long number into android properties |
| * |
| * @param name - property name, value - long value to be saved into property |
| */ |
| void ml_set_long_property(const char *name, long int value){ |
| char tmp[255]; |
| sprintf(tmp, "%ld", value); |
| property_set(name, tmp); |
| } |
| |
| /** |
| * ml_long_property |
| * This fucntion return android property as long number |
| * |
| * @param name - property name |
| * @return Long numer stored in property, or 0 incase property doesn't exist |
| */ |
| long int ml_get_long_property(const char *name){ |
| long int value = 0; |
| char tmp[255]; |
| property_get(name, tmp, "0"); |
| value = strtol(tmp, NULL, 10); |
| return value; |
| } |
| |
| /** |
| * ml_set_long_property |
| * Sets value of the long number into android properties |
| * |
| * @param name - property name, value - unsigned long value to be saved into property |
| */ |
| void ml_set_ulong_long_property(const char *name, unsigned long long value){ |
| char tmp[255]; |
| sprintf(tmp, "%llu", value); |
| property_set(name, tmp); |
| } |
| |
| /** |
| * ml_get_long_property |
| * This fucntion return android property as long number |
| * |
| * @param name - property name |
| * @return unsigned Long long numer stored in property, or 0 incase property doesn't exist |
| */ |
| unsigned long long ml_get_ulong_long_property(const char *name) { |
| unsigned long long value = 0; |
| char tmp[255]; |
| property_get(name, tmp, "0"); |
| value = strtoull(tmp, NULL, 10); |
| return value; |
| } |
| |
| /** |
| * ml_get_files_size |
| * This fucntion return files size on partition. Number of block |
| * used multuply by block size (512) |
| * |
| * @param filenames - string with file names to be calculated |
| * @return Number of bytes actually used by the system to have those file/dir on partition. |
| */ |
| unsigned long long ml_get_files_size(char *filenames){ |
| char * pch; |
| char *saveptr; |
| unsigned long long sum = 0; |
| pch = strtok_r(filenames," ,\n\r", &saveptr); |
| while (pch != NULL) |
| { |
| sum += ml_get_dir_size(pch); |
| pch = strtok_r(NULL," ,\n\r", &saveptr); |
| } |
| return sum; |
| } |
| /** |
| * ml_get_dir_size |
| * This fucntion return directory size on partition. Number of |
| * block used multuply by block size (512) |
| * |
| * @param filename - name of the directory/file |
| * @return Number of bytes actually used by the system to have this file/dir on partition. |
| */ |
| unsigned long long ml_get_dir_size(char *filename){ |
| struct stat statbuf; |
| unsigned long long sum = 0; |
| |
| if (stat(filename, &statbuf) != 0) { |
| return 0; |
| } |
| // value received in blocks, every block is 512 bytes. |
| sum = statbuf.st_blocks*512; |
| |
| if (S_ISLNK(statbuf.st_mode) || S_ISREG(statbuf.st_mode)) { |
| return sum; |
| } |
| |
| if (S_ISDIR(statbuf.st_mode)) { |
| DIR *dir; |
| struct dirent *entry; |
| char *newfile; |
| |
| dir = opendir(filename); |
| if (!dir) { |
| return sum; |
| } |
| |
| while ((entry = readdir(dir))) { |
| //ignore current dir and parent dir |
| if((strcmp(entry->d_name,"..")==0) || (strcmp(entry->d_name,".")==0)) |
| continue; |
| newfile = (char *)malloc(sizeof(char)*(strlen(filename)+strlen(entry->d_name)+2)); |
| if (newfile == NULL) |
| continue; |
| sprintf(newfile,"%s/%s", filename, entry->d_name); |
| sum += ml_get_dir_size(newfile); |
| free(newfile); |
| } |
| closedir(dir); |
| } |
| |
| return sum; |
| } |
| |
| /** |
| * Reads string from file |
| * |
| * @param fname file name to read from |
| * @param str pointer to buffer |
| * @param len length to read |
| * @param dopad true/false - if true then str will be pad to |
| * to fit requested length |
| * |
| * @return In case of success str pointer returned |
| */ |
| char *ml_file2str(char *fname, char *str, size_t len, int dopad) |
| { |
| int fd = -1, retries = 5; |
| char *ret = str; |
| char *sstr = str; |
| size_t slen = len, total = 0; |
| ssize_t rlen = 0; |
| |
| while ((fd = open(fname, O_RDONLY | O_NONBLOCK)) == -1) { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed open %s %s\n", fname, strerror(errno)); |
| ret = NULL; |
| goto mexit; |
| } |
| |
| retries = 5; |
| do { |
| rlen = read(fd, sstr, slen); |
| if (rlen >= 0) { |
| sstr += rlen; |
| slen -= rlen; |
| total += rlen; |
| } else { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| |
| ml_log_error("failed read %s %s rlen %lx\n", fname, strerror(errno), rlen); |
| ret = NULL; |
| goto mexit; |
| } |
| } while (slen > 0 && rlen > 0); |
| |
| mexit: |
| |
| if (ret == NULL) { |
| str[0] = 0; |
| if (dopad) |
| ml_pad_string('0', str, len); |
| } |
| |
| if (fd != -1) { |
| close(fd); |
| } |
| |
| if (ret && total) { |
| // Ensure NULL termination |
| if (total < len) { |
| //If last char is new line remove it |
| if (str[total-1] < 32) { |
| str[total-1] = 0; |
| } else { |
| str[total] = 0; |
| } |
| } else { |
| str[len - 1] = 0; |
| } |
| if(dopad) |
| ml_pad_string('0', str, len); |
| } |
| return ret; |
| } |
| |
| /** |
| * Atomicaly writes string to file |
| * NOTE: if (strlen() + 1) of the strings doesn't match passed length |
| * then output will be padded with ascii 'o' |
| * i.e. if passed "abla" but len == 10 the output 'abla00000\0' |
| * NOTE: Side effect - original string might be left padded |
| * after function execution |
| * @param fname Output file name |
| * @param str ascii string to write down |
| * @param len length to write - pay attention on strlen() |
| * |
| * @return In case of success str pointer returned |
| */ |
| char *ml_str2file(char *fname, char *str, size_t len) |
| { |
| int fd = -1, retries = 5; |
| char fname_tmp[128]; |
| char *sstr = str; |
| size_t slen = len; |
| ssize_t rlen = 0; |
| |
| snprintf(fname_tmp, 127, "%s.tmp", fname); |
| |
| while ((fd = open(fname_tmp, O_CREAT | O_WRONLY | O_TRUNC, 0660)) == -1) { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed open %s %s\n", fname_tmp, strerror(errno)); |
| return NULL; |
| } |
| |
| ml_pad_string('0', str, len); |
| retries = 5; |
| |
| do { |
| rlen = write(fd, sstr, slen); |
| |
| if (rlen >= 0) { |
| sstr += rlen; |
| slen -= rlen; |
| } else { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed write to %s %s\n", fname_tmp, strerror(errno)); |
| close(fd); |
| return NULL; |
| } |
| } while (rlen > 0 && slen > 0); |
| |
| close(fd); |
| if (rename(fname_tmp, fname)) { |
| ml_log_error("failed rename '%s %s' - %s\n", fname_tmp, fname, strerror(errno)); |
| return NULL; |
| } |
| sync(); |
| return str; |
| } |
| |
| char *ml_get_uniqid(struct ml_uniqid *unqd) |
| { |
| //Mutex protection against recreating same unique id |
| pthread_mutex_lock(&hawk_unique_id_mutex); |
| struct timeval detail_time; |
| static int srandom_seed = 0; |
| unsigned int rand; |
| struct ml_uniqid tmp; |
| |
| if (!srandom_seed) { |
| gettimeofday(&detail_time, NULL); |
| srandom_seed = 1; |
| srandom(detail_time.tv_usec); |
| } |
| |
| ml_get_imei(&tmp.imei); |
| ml_get_bld_vers(&tmp.vers); |
| |
| rand = ((unsigned int)random()) % 10000; |
| snprintf(tmp.rand.s, sizeof(tmp.rand), "%04d", rand); |
| |
| ml_get_mseq(&tmp.mseq); |
| snprintf((char *)unqd, sizeof(*unqd), "%s_%s_%s_%s", tmp.imei.s, tmp.vers.s, tmp.mseq.s, tmp.rand.s); |
| |
| pthread_mutex_unlock(&hawk_unique_id_mutex); |
| return (char *)unqd; |
| } |
| |
| char *ml_get_imei(struct ml_imei *m) |
| { |
| if (g_imei_good) { |
| memcpy(m->s, g_imei.s, sizeof(g_imei.s)); |
| return m->s; |
| } |
| |
| property_get(ML_IMEI_PROP, m->s, "000000000000000"); |
| if (strcmp(m->s, "000000000000000")) { |
| memcpy(g_imei.s, m->s, sizeof(m->s)); |
| g_imei_good = 1; |
| return m->s; |
| } |
| ml_log_info("IMEI not found\n"); |
| return NULL; |
| } |
| |
| /** |
| * Receive boot sequence number |
| * Based on static variable and file. |
| */ |
| int ml_get_boot_sequence(void) |
| { |
| char boot_seq[10] = {0}; |
| static int boot_seq_no = 0; |
| |
| //If static value exist then return it |
| if (boot_seq_no) |
| return boot_seq_no; |
| |
| //read sequnece number from property |
| property_get(ML_BOOTSEQ_PROP, boot_seq, "0"); |
| boot_seq_no = (atoi(boot_seq) + 1) % 10000; |
| |
| snprintf(boot_seq, sizeof(boot_seq), "%04d", boot_seq_no); |
| property_set(ML_BOOTSEQ_PROP, boot_seq); |
| |
| return boot_seq_no; |
| } |
| |
| char *ml_get_mseq(struct ml_mseq *m) |
| { |
| int seq = 0; |
| struct ml_mseq tmp; |
| |
| property_get(ML_MSEQ_PROP, m->s, "0000"); |
| |
| if (m->s) |
| seq = atoi(m->s); |
| |
| snprintf(tmp.s, sizeof(tmp.s), "%04d", (seq + 1) % 10000); |
| property_set(ML_MSEQ_PROP, tmp.s); |
| |
| return m->s; |
| } |
| |
| char *ml_get_vers(struct ml_vers *m) |
| { |
| char *ret; |
| |
| if (g_vers_good) { |
| memcpy(m->s, g_vers.s, sizeof(g_vers.s)); |
| return m->s; |
| } |
| |
| ret = ml_file2str(ML_VERS_FNAME, m->s, sizeof(m->s), 0); |
| |
| if (ret) { |
| memcpy(g_vers.s, m->s, sizeof(m->s)); |
| g_vers_good = 1; |
| } |
| |
| return ret; |
| } |
| |
| char *ml_get_bld_vers(struct ml_bld_vers *m) |
| { |
| struct ml_vers full_ver; |
| char *bld_num = NULL; |
| char *ver_num = NULL; |
| |
| if (g_bld_vers_good) { |
| memcpy(m->s, g_bld_vers.s, sizeof(g_bld_vers.s)); |
| return m->s; |
| } |
| |
| ver_num = ml_get_vers(&full_ver); |
| |
| /* check if full version exist */ |
| if(!ver_num) |
| return NULL; |
| |
| /* build number is xxxx in _branch_bldxxxx */ |
| bld_num = strstr(ver_num, "_bld"); |
| |
| if (bld_num){ |
| bld_num = bld_num + sizeof("_bld") - 1; |
| strncpy(m->s, bld_num, sizeof(m->s) - 1); |
| strncpy(g_bld_vers.s, bld_num, sizeof(g_bld_vers.s) - 1); |
| } else { |
| strcpy(m->s, "0000"); |
| strcpy(g_bld_vers.s, "0000"); |
| } |
| |
| g_bld_vers_good = 1; |
| |
| return m->s; |
| } |
| |
| char *ml_get_cp_ver(struct ml_cp_ver *m) |
| { |
| if (g_cp_ver_good) { |
| memcpy(m->s, g_cp_ver.s, sizeof(g_cp_ver.s)); |
| return m->s; |
| } |
| |
| property_get(ML_CP_VER_PROP, m->s, "000000"); |
| if (strcmp(m->s, "000000")) { |
| memcpy(g_cp_ver.s, m->s, sizeof(m->s)); |
| g_cp_ver_good = 1; |
| return m->s; |
| } |
| ml_log_info("CP_VER not found\n"); |
| return NULL; |
| } |
| |
| char *ml_update_imei(struct ml_imei *m) |
| { |
| struct ml_imei imei; |
| char *ret; |
| |
| if ((ret = ml_get_imei(&imei))) { |
| ml_log_info("IMEI: exists %s\n", imei.s); |
| return ret; |
| } |
| |
| m->s[sizeof(m->s) - 1] = 0; |
| |
| property_set(ML_IMEI_PROP, m->s); |
| |
| memcpy(g_imei.s, m->s, sizeof(m->s)); |
| g_imei_good = 1; |
| ml_log_info("IMEI: updated %s\n", m->s); |
| return m->s; |
| } |
| |
| char *ml_update_cp_ver(struct ml_cp_ver *m) |
| { |
| struct ml_cp_ver cp_ver; |
| char *ret; |
| |
| if ((ret = ml_get_cp_ver(&cp_ver))) { |
| ml_log_info("CP_VER: exists %s\n", cp_ver.s); |
| return ret; |
| } |
| |
| m->s[sizeof(m->s) - 1] = 0; |
| |
| property_set(ML_CP_VER_PROP, m->s); |
| |
| memcpy(g_cp_ver.s, m->s, sizeof(m->s)); |
| g_cp_ver_good = 1; |
| ml_log_info("CP_VER: updated %s\n", m->s); |
| return m->s; |
| } |
| |
| char *ml_extract_cp_from_full_vers(void) |
| { |
| struct ml_vers full_ver; |
| struct ml_cp_ver cp_ver; |
| int i = 0; |
| int cp_type = ml_get_property(ML_CP_TYPE_PROP); |
| char *str_tmp = ml_get_vers(&full_ver); |
| char *token = NULL; |
| |
| if(!str_tmp) |
| return NULL; |
| |
| while ((token=strsep(&str_tmp, "_"))&& (i < (cp_type+2))) |
| i++; |
| |
| if(token) |
| strncpy(cp_ver.s, token, sizeof(cp_ver.s) - 1); |
| |
| return ml_update_cp_ver(&cp_ver); |
| } |
| |
| /** |
| * ************************************************************ |
| * mll_string.c |
| * ************************************************************ |
| */ |
| |
| /** |
| * Trim leading and trailing whitespaces |
| * |
| * @param str string to modify |
| * |
| * @return pointer to in-place modified string |
| */ |
| char *ml_chomp(char *str) |
| { |
| char *p = str; |
| int length; |
| |
| if (!p || (length = strlen(p)) == 0) |
| return p; |
| |
| // chomp space chars from end |
| while (length && isspace(p[length - 1])) { |
| p[--length] = 0; |
| } |
| // chomp space chars from start |
| while (*p && isspace(*p)) { |
| ++p; |
| --length; |
| } |
| |
| memmove(str, p, length + 1); |
| return str; |
| } |
| |
| void ml_pad_string(char pad, char *str, size_t len) |
| { |
| size_t i; |
| |
| if (!str) |
| return; |
| |
| str[len - 1] = 0; |
| for (i = strlen(str); i < (len - 1); i++) |
| str[i] = pad; |
| } |
| |
| /** |
| * ************************************************************ |
| * mll_system.c |
| * ************************************************************ |
| */ |
| |
| static void ml_child(char *cmd) |
| { |
| char *argv[4]; |
| |
| if (!cmd) { |
| ml_log_info("Got empty cmd line\n"); |
| exit(-1); |
| } |
| |
| argv[0] = "sh"; |
| argv[1] = "-c"; |
| argv[2] = cmd; |
| argv[3] = NULL; |
| |
| if (execvp(argv[0], argv)) { |
| ml_log_info("executing %s failed: %s\n", argv[0], strerror(errno)); |
| exit(-1); |
| } |
| } |
| |
| int ml_system_retry(int max_tries, int timeout, char *cmd) |
| { |
| int ret = -1, i = 0, tries = max_tries > 0 ? max_tries : 1; |
| |
| while (ret != 0 && i++ < tries) { |
| ret = ml_system(timeout, cmd); |
| } |
| return ret; |
| } |
| |
| int ml_system(int timeout, char *cmd) |
| { |
| int timo, status = 0xAAAA, i, ret = -1, forced_kill = 0; |
| struct sysinfo info1, info2; |
| pid_t end_pid, child_pid; |
| char pdetails[64], edt[192], strerr[128]; |
| |
| timo = (timeout < 1 || timeout > ML_SYSTEM_MAX_TIMEOUT) ? ML_SYSTEM_MAX_TIMEOUT : timeout; |
| |
| //sprintf(pdetails, "[%d:%d]", getpid(), gettid()); |
| sprintf(pdetails, "[%d]", getpid()); |
| if (!cmd) { |
| ml_log_info("%s Got empty cmd line\n", pdetails); |
| return -1; |
| } |
| info1.uptime = info2.uptime = 0; |
| |
| ml_log_info("%s cmdstart (timo %d sec): ==%s==\n", pdetails, timo, cmd); |
| sysinfo(&info1); |
| child_pid = fork(); |
| if (child_pid < 0) { |
| ml_log_info("%s Failed to fork\n", pdetails); |
| } else if (child_pid == 0) { |
| ml_child(cmd); |
| } |
| |
| for (i = 0; i <= timo; i++) { |
| while ((end_pid = waitpid(child_pid, &status, WNOHANG | WUNTRACED)) == -1 && errno == EINTR) { |
| /*signal interrupt */ }; |
| |
| if (end_pid == 0) { /* child still running */ |
| if (i < timo) { |
| sleep(1); |
| continue; |
| } |
| /*Force sub-process shutdown */ |
| kill(child_pid, SIGKILL); |
| forced_kill = 1; |
| while ((end_pid = wait(&status)) == -1 && errno == EINTR) { |
| /*signal interrupt */ }; |
| } |
| if (end_pid == -1) { /* error calling waitpid */ |
| strerror_r(errno, strerr, sizeof(strerr)); |
| snprintf(edt, sizeof(edt), "waitpid() fail: %s (%d)", strerr, errno); |
| break; |
| } |
| |
| /* child ended */ |
| if (WIFEXITED(status)) { |
| snprintf(edt, sizeof(edt), "terminated by exit(%d)", WEXITSTATUS(status)); |
| ret = WEXITSTATUS(status) ? -1 : 0; |
| break; |
| } |
| if (WIFSIGNALED(status)) { |
| snprintf(edt, sizeof(edt), "terminated by signal %d", WTERMSIG(status)); |
| break; |
| } |
| if (WIFSTOPPED(status)) { |
| snprintf(edt, sizeof(edt), "stopped by signal %d", WSTOPSIG(status)); |
| break; |
| } |
| } |
| |
| sysinfo(&info2); |
| i = (int)(info2.uptime - info1.uptime); |
| ml_log_info("%s cmdend: %s (%s) : %d seconds\n", pdetails, edt, forced_kill ? "forced_kill":"self exited", i ? i : 1); |
| return ret; |
| } |
| |
| /* Check if path exist, return 1 if path exist */ |
| int ml_check_path(const char* path, int r, int w) |
| { |
| int mode = 0; |
| |
| if (r) |
| mode |= R_OK; |
| if (w) |
| mode |= W_OK; |
| |
| if (access(path, mode)){ |
| ml_log_error("Can't access %s. %s\n", path, strerror(errno)); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Check if directory exist, and create it if not, return 1 for success */ |
| int ml_dir_update(char *dir2add) |
| { |
| struct stat buf; |
| |
| if (stat(dir2add, &buf) != 0) { |
| if (mkdir(dir2add, 0777) != 0) { |
| ml_log_error("Failed to mkdir %s. %s\n", dir2add, strerror(errno)); |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |
| |
| /** |
| * ************************************************************ |
| * mll_log.c |
| * ************************************************************ |
| */ |
| |
| pthread_mutex_t mll_log_plock = PTHREAD_MUTEX_INITIALIZER; |
| |
| int ml_max_log = ML_LOG_INFO; |
| |
| extern int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...); |
| |
| void ml_set_log_level(int logLevel) |
| { |
| ml_max_log = logLevel; |
| } |
| |
| int ml_get_log_level() |
| { |
| return ml_max_log; |
| } |
| |
| static void ml_sout(FILE * fout, char *buf) |
| { |
| if (!fout) |
| return; |
| fwrite(buf, strlen(buf) + 1, 1, fout); |
| } |
| |
| static struct { |
| char *tag; |
| char buf[32]; |
| } ltag = { |
| .tag = 0,.buf = { |
| 0} |
| }; |
| |
| char *ml_get_local_tag() |
| { |
| FILE *f; |
| |
| if (ltag.tag) |
| return ltag.tag; |
| |
| pthread_mutex_lock(&mll_log_plock); |
| if ((f = fopen("/proc/self/status", "r")) == NULL) |
| goto done; |
| |
| if (1 != fscanf(f, "Name: %s", ltag.buf)) |
| goto done; |
| |
| ltag.tag = ltag.buf; |
| |
| done: |
| pthread_mutex_unlock(&mll_log_plock); |
| if (!ltag.tag) |
| return "ML_LOG"; |
| return ltag.tag; |
| } |
| |
| void ml_log(int dest, int level, const char *tag, const char *fmt, ...) |
| { |
| va_list vl; |
| char buf[1024]; |
| size_t buf_size; |
| |
| if (!tag) |
| tag = ml_get_local_tag(); |
| |
| va_start(vl, fmt); |
| buf_size = vsnprintf(buf, 1024, fmt, vl); |
| va_end(vl); |
| |
| if (dest & MLL_LOGCAT) |
| __android_log_printf(LOG_ID_RADIO, level, buf); |
| |
| if (dest & MLL_KMSG) { |
| static int dev_kmsg = -1; |
| pthread_mutex_lock(&mll_log_plock); |
| if (dev_kmsg < 0) |
| dev_kmsg = open("/dev/kmsg", O_RDWR | O_SYNC); |
| if (dev_kmsg >= 0) { |
| buf[buf_size] = '\0'; |
| write(dev_kmsg, buf, buf_size); |
| } |
| pthread_mutex_unlock(&mll_log_plock); |
| } |
| |
| if (dest & MLL_CONSOLE) { |
| static FILE *dev_console = NULL; |
| pthread_mutex_lock(&mll_log_plock); |
| if (!dev_console) |
| dev_console = fopen("/dev/console", "w"); |
| ml_sout(dev_console, buf); |
| pthread_mutex_unlock(&mll_log_plock); |
| } |
| |
| if (dest & MLL_STDERR) { |
| pthread_mutex_lock(&mll_log_plock); |
| ml_sout(stderr, buf); |
| pthread_mutex_unlock(&mll_log_plock); |
| } |
| } |
| |
| /** |
| * ************************************************************ |
| * mll_fifo.c |
| * ************************************************************ |
| */ |
| |
| int ml_mounted_update(char *mountdir, char *dir2add) |
| { |
| char line[128 * 3], dev[128], dir[128]; |
| struct stat buf; |
| FILE *filed; |
| int ret = 0; |
| |
| if (!mountdir) { |
| ml_log_error("no param supplied\n"); |
| return 0; |
| } |
| |
| filed = fopen("/proc/mounts", "r"); |
| if (!filed) { |
| ml_log_error("Failed open %s %s \n", "/proc/mounts", strerror(errno)); |
| exit(1); |
| } |
| while (fgets(line, sizeof(line), filed)) { |
| if (sscanf(line, "%128s %128s", dev, dir) < 2) |
| continue; |
| |
| if (strcmp(mountdir, dir) != 0) |
| continue; |
| |
| if (!dir2add) { |
| ret = 1; |
| break; |
| } |
| |
| if (stat(dir2add, &buf) != 0) { |
| if (mkdir(dir2add, 0777) != 0) { |
| ml_log_error("Failed to mkdir %s. %s\n", dir2add, strerror(errno)); |
| break; |
| } |
| } else if (!S_ISDIR(buf.st_mode)) { |
| ml_log_error("Gen failure %s is not directory.\n", dir2add); |
| break; |
| } |
| ret = 1; |
| break; |
| } |
| |
| fclose(filed); |
| return ret; |
| } |
| |
| int ml_create_fifo(struct ml_fifo *fifo) |
| { |
| int ret, retries; |
| struct stat buf; |
| |
| ret = mkfifo(fifo->fname, 0666); |
| if (ret != 0) { |
| if (errno == EEXIST) { |
| if (stat(fifo->fname, &buf) != 0 || !S_ISFIFO(buf.st_mode)) { |
| ml_log_error("%s is not fifo\n", fifo->fname); |
| return -1; |
| } |
| } else { |
| ml_log_error("fifo not created %s\n", strerror(errno)); |
| return -1; |
| } |
| } |
| retries = 5; |
| /* Hold two open file descriptors on FIFO for select() to work correctly */ |
| while ((fifo->fdr = open(fifo->fname, O_RDONLY | O_NONBLOCK)) == -1) { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed open %s %s\n", fifo->fname, strerror(errno)); |
| return -1; |
| } |
| retries = 5; |
| while ((fifo->fdw = open(fifo->fname, O_WRONLY | O_NONBLOCK)) == -1) { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| close(fifo->fdr); |
| ml_log_error("failed open %s %s\n", fifo->fname, strerror(errno)); |
| return -1; |
| } |
| return 0; |
| } |
| |
| int ml_flock(int fd, char *fname, int mode) |
| { |
| int retries = 5, ret; |
| |
| while (1) { |
| ret = flock(fd, mode); |
| if (ret == 0) |
| return 0; |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed flock [%d %s mode %d] on %s\n", fd, fname, mode, strerror(errno)); |
| break; |
| } |
| return -1; |
| } |
| |
| int ml_write_fifo(struct ml_fifo *fifo, char *str, int len) |
| { |
| int retries = 5; |
| char *sstr = str; |
| size_t slen = len; |
| ssize_t rlen = 0; |
| |
| retries = 5; |
| |
| ml_flock(fifo->fdw, fifo->fname, LOCK_EX); |
| do { |
| rlen = write(fifo->fdw, str, len); |
| if (rlen >= 0) { |
| sstr += rlen; |
| slen -= rlen; |
| } else { |
| if (errno == EINTR && --retries > 0) |
| continue; |
| ml_log_error("failed write to %s %s\n", fifo->fname, strerror(errno)); |
| break; |
| } |
| } while (rlen > 0 && slen > 0); |
| |
| fflush(NULL); |
| ml_flock(fifo->fdw, fifo->fname, LOCK_UN); |
| /* should be 0 if all written OK */ |
| rlen = len - slen; |
| if (!rlen) { |
| ml_log_error("data not written ok to %s - %d/%d bytes written\n", fifo->fname, (int)rlen, (int)len); |
| } |
| return rlen; |
| } |
| |
| char *ml_read_fifo(struct ml_fifo *fifo, char *str, int len, struct timeval *timeout) |
| { |
| fd_set set; |
| struct timeval tm = *timeout; |
| char tmp[1], *sstr = str; |
| size_t slen = len; |
| ssize_t rlen = 0; |
| int ret; |
| |
| while (1) { |
| FD_ZERO(&set); |
| FD_SET(fifo->fdr, &set); |
| |
| ret = select(fifo->fdr + 1, &set, NULL, NULL, &tm); |
| if (ret < 0) { |
| if (errno != EINTR) { |
| ml_log_error("select failed with '%s'\n", strerror(errno)); |
| exit(1); |
| } |
| continue; |
| } |
| if (ret == 0) { |
| /*timeout */ |
| return NULL; |
| } |
| |
| rlen = read(fifo->fdr, tmp, 1); |
| if (rlen > 0) { |
| *sstr = *tmp; |
| |
| if (*tmp == '\0' || *tmp == '\n' || *tmp == '\r') { |
| *sstr = '\0'; |
| return str; |
| } else { |
| sstr += rlen; |
| slen -= rlen; |
| } |
| } else { |
| ml_log_error("read interrupted ? '%s'\n", strerror(errno)); |
| continue; |
| } |
| if (slen < 2) { |
| ml_log_error("Message to big '%s'\n", str); |
| *str = 0; |
| return NULL; |
| } |
| } |
| |
| return str; |
| } |
| |