1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "nanoapp_cmd" 18 19 #include <assert.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <inttypes.h> 23 #include <signal.h> 24 #include <stdbool.h> 25 #include <stdint.h> 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <sys/stat.h> 29 #include <sys/types.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include <android/log.h> 34 35 #include <nanohub/nanohub.h> 36 #include <eventnums.h> 37 #include <sensType.h> 38 39 #define SENSOR_RATE_ONCHANGE 0xFFFFFF01UL 40 #define SENSOR_RATE_ONESHOT 0xFFFFFF02UL 41 #define SENSOR_HZ(_hz) ((uint32_t)((_hz) * 1024.0f)) 42 #define MAX_APP_NAME_LEN 32 43 #define MAX_INSTALL_CNT 8 44 #define MAX_UNINSTALL_CNT 8 45 #define MAX_DOWNLOAD_RETRIES 4 46 #define UNINSTALL_CMD "uninstall" 47 48 #define NANOHUB_HAL_EXT_APPS_ON 0 49 #define NANOHUB_HAL_EXT_APPS_OFF 1 50 #define NANOHUB_HAL_EXT_APP_DELETE 2 51 52 #define LOGE(fmt, ...) do { \ 53 __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, fmt, ##__VA_ARGS__); \ 54 printf(fmt "\n", ##__VA_ARGS__); \ 55 } while (0) 56 57 enum ConfigCmds 58 { 59 CONFIG_CMD_DISABLE = 0, 60 CONFIG_CMD_ENABLE = 1, 61 CONFIG_CMD_FLUSH = 2, 62 CONFIG_CMD_CFG_DATA = 3, 63 CONFIG_CMD_CALIBRATE = 4, 64 }; 65 66 struct ConfigCmd 67 { 68 uint32_t evtType; 69 uint64_t latency; 70 uint32_t rate; 71 uint8_t sensorType; 72 uint8_t cmd; 73 uint16_t flags; 74 uint8_t data[]; 75 } __attribute__((packed)); 76 77 struct HalCmd 78 { 79 struct HostMsgHdr hdr; 80 uint8_t cmd; 81 uint64_t appId; 82 } __attribute__((packed)); 83 84 struct App 85 { 86 uint32_t num; 87 uint64_t id; 88 uint32_t version; 89 uint32_t size; 90 }; 91 92 struct LedsCfg { 93 uint32_t led_num; 94 uint32_t value; 95 } __attribute__((packed)); 96 97 static int setType(struct ConfigCmd *cmd, char *sensor) 98 { 99 if (strcmp(sensor, "accel") == 0) { 100 cmd->sensorType = SENS_TYPE_ACCEL; 101 } else if (strcmp(sensor, "gyro") == 0) { 102 cmd->sensorType = SENS_TYPE_GYRO; 103 } else if (strcmp(sensor, "mag") == 0) { 104 cmd->sensorType = SENS_TYPE_MAG; 105 } else if (strcmp(sensor, "uncal_accel") == 0) { 106 cmd->sensorType = SENS_TYPE_ACCEL; 107 } else if (strcmp(sensor, "uncal_gyro") == 0) { 108 cmd->sensorType = SENS_TYPE_GYRO; 109 } else if (strcmp(sensor, "uncal_mag") == 0) { 110 cmd->sensorType = SENS_TYPE_MAG; 111 } else if (strcmp(sensor, "als") == 0) { 112 cmd->sensorType = SENS_TYPE_ALS; 113 } else if (strcmp(sensor, "prox") == 0) { 114 cmd->sensorType = SENS_TYPE_PROX; 115 } else if (strcmp(sensor, "baro") == 0) { 116 cmd->sensorType = SENS_TYPE_BARO; 117 } else if (strcmp(sensor, "temp") == 0) { 118 cmd->sensorType = SENS_TYPE_TEMP; 119 } else if (strcmp(sensor, "ambient_temp") == 0) { 120 cmd->sensorType = SENS_TYPE_AMBIENT_TEMP; 121 } else if (strcmp(sensor, "orien") == 0) { 122 cmd->sensorType = SENS_TYPE_ORIENTATION; 123 } else if (strcmp(sensor, "gravity") == 0) { 124 cmd->sensorType = SENS_TYPE_GRAVITY; 125 } else if (strcmp(sensor, "geomag") == 0) { 126 cmd->sensorType = SENS_TYPE_GEO_MAG_ROT_VEC; 127 } else if (strcmp(sensor, "linear_acc") == 0) { 128 cmd->sensorType = SENS_TYPE_LINEAR_ACCEL; 129 } else if (strcmp(sensor, "rotation") == 0) { 130 cmd->sensorType = SENS_TYPE_ROTATION_VECTOR; 131 } else if (strcmp(sensor, "game") == 0) { 132 cmd->sensorType = SENS_TYPE_GAME_ROT_VECTOR; 133 } else if (strcmp(sensor, "win_orien") == 0) { 134 cmd->sensorType = SENS_TYPE_WIN_ORIENTATION; 135 cmd->rate = SENSOR_RATE_ONCHANGE; 136 } else if (strcmp(sensor, "tilt") == 0) { 137 cmd->sensorType = SENS_TYPE_TILT; 138 cmd->rate = SENSOR_RATE_ONCHANGE; 139 } else if (strcmp(sensor, "step_det") == 0) { 140 cmd->sensorType = SENS_TYPE_STEP_DETECT; 141 cmd->rate = SENSOR_RATE_ONCHANGE; 142 } else if (strcmp(sensor, "step_cnt") == 0) { 143 cmd->sensorType = SENS_TYPE_STEP_COUNT; 144 cmd->rate = SENSOR_RATE_ONCHANGE; 145 } else if (strcmp(sensor, "double_tap") == 0) { 146 cmd->sensorType = SENS_TYPE_DOUBLE_TAP; 147 cmd->rate = SENSOR_RATE_ONCHANGE; 148 } else if (strcmp(sensor, "flat") == 0) { 149 cmd->sensorType = SENS_TYPE_FLAT; 150 cmd->rate = SENSOR_RATE_ONCHANGE; 151 } else if (strcmp(sensor, "anymo") == 0) { 152 cmd->sensorType = SENS_TYPE_ANY_MOTION; 153 cmd->rate = SENSOR_RATE_ONCHANGE; 154 } else if (strcmp(sensor, "nomo") == 0) { 155 cmd->sensorType = SENS_TYPE_NO_MOTION; 156 cmd->rate = SENSOR_RATE_ONCHANGE; 157 } else if (strcmp(sensor, "sigmo") == 0) { 158 cmd->sensorType = SENS_TYPE_SIG_MOTION; 159 cmd->rate = SENSOR_RATE_ONESHOT; 160 } else if (strcmp(sensor, "gesture") == 0) { 161 cmd->sensorType = SENS_TYPE_GESTURE; 162 cmd->rate = SENSOR_RATE_ONESHOT; 163 } else if (strcmp(sensor, "hall") == 0) { 164 cmd->sensorType = SENS_TYPE_HALL; 165 cmd->rate = SENSOR_RATE_ONCHANGE; 166 } else if (strcmp(sensor, "vsync") == 0) { 167 cmd->sensorType = SENS_TYPE_VSYNC; 168 cmd->rate = SENSOR_RATE_ONCHANGE; 169 } else if (strcmp(sensor, "activity") == 0) { 170 cmd->sensorType = SENS_TYPE_ACTIVITY; 171 cmd->rate = SENSOR_RATE_ONCHANGE; 172 } else if (strcmp(sensor, "twist") == 0) { 173 cmd->sensorType = SENS_TYPE_DOUBLE_TWIST; 174 cmd->rate = SENSOR_RATE_ONCHANGE; 175 } else if (strcmp(sensor, "leds") == 0) { 176 cmd->sensorType = SENS_TYPE_LEDS; 177 } else if (strcmp(sensor, "leds_i2c") == 0) { 178 cmd->sensorType = SENS_TYPE_LEDS_I2C; 179 } else if (strcmp(sensor, "humidity") == 0) { 180 cmd->sensorType = SENS_TYPE_HUMIDITY; 181 } else { 182 return 1; 183 } 184 185 return 0; 186 } 187 188 bool drain = false; 189 bool stop = false; 190 char *buf; 191 int nread, buf_size = 2048; 192 struct App apps[32]; 193 uint8_t appCount; 194 char appsToInstall[MAX_INSTALL_CNT][MAX_APP_NAME_LEN+1]; 195 uint64_t appsToUninstall[MAX_UNINSTALL_CNT]; 196 197 void sig_handle(__attribute__((unused)) int sig) 198 { 199 assert(sig == SIGINT); 200 printf("Terminating...\n"); 201 stop = true; 202 } 203 204 FILE *openFile(const char *fname, const char *mode) 205 { 206 FILE *f = fopen(fname, mode); 207 if (f == NULL) { 208 LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno)); 209 } 210 return f; 211 } 212 213 void parseInstalledAppInfo() 214 { 215 FILE *fp; 216 char *line = NULL; 217 size_t len; 218 ssize_t numRead; 219 220 appCount = 0; 221 222 fp = openFile("/sys/class/nanohub/nanohub/app_info", "r"); 223 if (!fp) 224 return; 225 226 while ((numRead = getline(&line, &len, fp)) != -1) { 227 struct App *currApp = &apps[appCount++]; 228 sscanf(line, "app: %d id: %" PRIx64 " ver: %" PRIx32 " size: %" PRIx32 "\n", &currApp->num, &currApp->id, &currApp->version, &currApp->size); 229 } 230 231 fclose(fp); 232 233 if (line) 234 free(line); 235 } 236 237 struct App *findApp(uint64_t appId) 238 { 239 uint8_t i; 240 241 for (i = 0; i < appCount; i++) { 242 if (apps[i].id == appId) { 243 return &apps[i]; 244 } 245 } 246 247 return NULL; 248 } 249 250 int findAppIdByName(char *name, uint64_t *appId) 251 { 252 FILE *fp; 253 char *line = NULL; 254 size_t len; 255 ssize_t numRead; 256 int ret = 0; 257 258 fp = openFile("/vendor/firmware/napp_list.cfg", "r"); 259 if (!fp) 260 return -1; 261 262 while ((numRead = getline(&line, &len, fp)) != -1) { 263 char entry[MAX_APP_NAME_LEN+1]; 264 uint32_t appVersion; 265 266 sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", entry, appId, &appVersion); 267 268 if (strncmp(entry, name, MAX_APP_NAME_LEN) == 0) { 269 ret = 1; 270 break; 271 } 272 } 273 274 fclose(fp); 275 276 if (line) 277 free(line); 278 279 return ret; 280 } 281 282 int parseConfigAppInfo(int *installCnt, int *uninstallCnt) 283 { 284 FILE *fp; 285 char *line = NULL; 286 size_t len; 287 ssize_t numRead; 288 289 fp = openFile("/vendor/firmware/napp_list.cfg", "r"); 290 if (!fp) 291 return -1; 292 293 parseInstalledAppInfo(); 294 295 *installCnt = *uninstallCnt = 0; 296 while (((numRead = getline(&line, &len, fp)) != -1) && (*installCnt < MAX_INSTALL_CNT) && (*uninstallCnt < MAX_UNINSTALL_CNT)) { 297 uint64_t appId; 298 uint32_t appVersion; 299 struct App *installedApp; 300 301 sscanf(line, "%" STRINGIFY(MAX_APP_NAME_LEN) "s %" PRIx64 " %" PRIx32 "\n", appsToInstall[*installCnt], &appId, &appVersion); 302 303 installedApp = findApp(appId); 304 if (strncmp(appsToInstall[*installCnt], UNINSTALL_CMD, MAX_APP_NAME_LEN) == 0) { 305 if (installedApp) { 306 appsToUninstall[*uninstallCnt] = appId; 307 (*uninstallCnt)++; 308 } 309 } else if (!installedApp || (installedApp->version < appVersion)) { 310 (*installCnt)++; 311 } 312 } 313 314 fclose(fp); 315 316 if (line) 317 free(line); 318 319 return *installCnt + *uninstallCnt; 320 } 321 322 bool fileWriteData(const char *fname, const void *data, size_t size) 323 { 324 int fd; 325 bool result; 326 327 fd = open(fname, O_WRONLY); 328 if (fd < 0) { 329 LOGE("Failed to open %s: err=%d [%s]", fname, errno, strerror(errno)); 330 return false; 331 } 332 333 result = true; 334 if ((size_t)write(fd, data, size) != size) { 335 LOGE("Failed to write to %s; err=%d [%s]", fname, errno, strerror(errno)); 336 result = false; 337 } 338 close(fd); 339 340 return result; 341 } 342 343 void downloadNanohub() 344 { 345 char c = '1'; 346 347 printf("Updating nanohub OS [if required]..."); 348 fflush(stdout); 349 if (fileWriteData("/sys/class/nanohub/nanohub/download_bl", &c, sizeof(c))) 350 printf("done\n"); 351 } 352 353 bool sendCmd(uint8_t cmd, uint64_t appId) 354 { 355 struct HalCmd msg; 356 357 msg.hdr.eventId = EVT_APP_FROM_HOST; 358 msg.hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0); 359 msg.hdr.len = sizeof(msg) - sizeof(msg.hdr); // payload length 360 msg.cmd = cmd; 361 memcpy(&msg.appId, &appId, sizeof(uint64_t)); 362 363 return fileWriteData("/dev/nanohub", &msg, sizeof(msg)); 364 } 365 366 int halCmd(uint8_t cmd, char *arg) 367 { 368 uint64_t appId; 369 char *endptr = arg + strlen(arg); 370 371 if (strcmp(arg, UNINSTALL_CMD) == 0) { 372 printf("%s is not a valid app name\n", arg); 373 return 1; 374 } 375 376 if ((findAppIdByName(arg, &appId) == 1) || (appId = strtoull(arg, &endptr, 16)) > 0) { 377 if (*endptr != '\0') { 378 printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg); 379 return 1; 380 } else if (cmd == NANOHUB_HAL_EXT_APPS_ON) 381 printf("Loading "); 382 else if (cmd == NANOHUB_HAL_EXT_APPS_OFF) 383 printf("Unloading "); 384 else if (cmd == NANOHUB_HAL_EXT_APP_DELETE) 385 printf("Deleting "); 386 else { 387 printf("Unrecognized cmd: %d\n", cmd); 388 return 1; 389 } 390 printf("\"0x%016" PRIx64 "\"...", appId); 391 fflush(stdout); 392 if (sendCmd(cmd, appId)) 393 printf("done\n"); 394 return 0; 395 } else { 396 printf("Couldn't find nanoapp '%s' in napp_list.cfg\n", arg); 397 return 1; 398 } 399 } 400 401 void removeApps(int updateCnt) 402 { 403 int i; 404 405 for (i = 0; i < updateCnt; i++) { 406 printf("Deleting \"0x%016" PRIx64 "\"...", appsToUninstall[i]); 407 fflush(stdout); 408 if (sendCmd(NANOHUB_HAL_EXT_APP_DELETE, appsToUninstall[i])) 409 printf("done\n"); 410 } 411 } 412 413 void downloadApps(int updateCnt) 414 { 415 int i; 416 417 for (i = 0; i < updateCnt; i++) { 418 printf("Downloading \"%s.napp\"...", appsToInstall[i]); 419 fflush(stdout); 420 if (fileWriteData("/sys/class/nanohub/nanohub/download_app", appsToInstall[i], strlen(appsToInstall[i]))) 421 printf("done\n"); 422 } 423 } 424 425 void eraseSharedArea() 426 { 427 char c = '1'; 428 429 printf("Erasing entire nanohub shared area..."); 430 fflush(stdout); 431 if (fileWriteData("/sys/class/nanohub/nanohub/erase_shared", &c, sizeof(c))) 432 printf("done\n"); 433 } 434 435 void resetHub() 436 { 437 char c = '1'; 438 439 printf("Resetting nanohub..."); 440 fflush(stdout); 441 if (fileWriteData("/sys/class/nanohub/nanohub/reset", &c, sizeof(c))) 442 printf("done\n"); 443 } 444 445 int main(int argc, char *argv[]) 446 { 447 struct ConfigCmd mConfigCmd; 448 struct ConfigCmd *pConfigCmd = &mConfigCmd; 449 size_t length = sizeof(mConfigCmd); 450 int fd; 451 int i; 452 453 if (argc < 3 && (argc < 2 || strcmp(argv[1], "download") != 0)) { 454 printf("usage: %s <action> <sensor> <data> -d\n", argv[0]); 455 printf(" action: config|cfgdata|calibrate|flush\n"); 456 printf(" sensor: (uncal_)accel|(uncal_)gyro|(uncal_)mag|als|prox|baro|temp|orien\n"); 457 printf(" gravity|geomag|linear_acc|rotation|game\n"); 458 printf(" win_orien|tilt|step_det|step_cnt|double_tap\n"); 459 printf(" flat|anymo|nomo|sigmo|gesture|hall|vsync\n"); 460 printf(" activity|twist|leds|leds_i2c|humidity|ambient_temp\n"); 461 printf(" data: config: <true|false> <rate in Hz> <latency in u-sec>\n"); 462 printf(" cfgdata: leds: led_num value\n"); 463 printf(" calibrate: [N.A.]\n"); 464 printf(" flush: [N.A.]\n"); 465 printf(" -d: if specified, %s will keep draining /dev/nanohub until cancelled.\n", argv[0]); 466 printf("usage: %s <cmd> [app]\n", argv[0]); 467 printf(" cmd: download|load|unload|delete\n"); 468 printf(" app: appId or name from napp_list.cfg\n"); 469 470 return 1; 471 } 472 473 if (strcmp(argv[1], "config") == 0) { 474 if (argc != 6 && argc != 7) { 475 printf("Wrong arg number\n"); 476 return 1; 477 } 478 if (argc == 7) { 479 if(strcmp(argv[6], "-d") == 0) { 480 drain = true; 481 } else { 482 printf("Last arg unsupported, ignored.\n"); 483 } 484 } 485 if (strcmp(argv[3], "true") == 0) 486 mConfigCmd.cmd = CONFIG_CMD_ENABLE; 487 else if (strcmp(argv[3], "false") == 0) { 488 mConfigCmd.cmd = CONFIG_CMD_DISABLE; 489 } else { 490 printf("Unsupported data: %s For action: %s\n", argv[3], argv[1]); 491 return 1; 492 } 493 mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; 494 mConfigCmd.rate = SENSOR_HZ((float)atoi(argv[4])); 495 mConfigCmd.latency = atoi(argv[5]) * 1000ull; 496 if (setType(&mConfigCmd, argv[2])) { 497 printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); 498 return 1; 499 } 500 } else if (strcmp(argv[1], "cfgdata") == 0) { 501 mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; 502 mConfigCmd.rate = 0; 503 mConfigCmd.latency = 0; 504 mConfigCmd.cmd = CONFIG_CMD_CFG_DATA; 505 if (setType(&mConfigCmd, argv[2])) { 506 printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); 507 return 1; 508 } 509 if (mConfigCmd.sensorType == SENS_TYPE_LEDS || 510 mConfigCmd.sensorType == SENS_TYPE_LEDS_I2C) { 511 struct LedsCfg mLedsCfg; 512 513 if (argc != 5) { 514 printf("Wrong arg number\n"); 515 return 1; 516 } 517 length = sizeof(struct ConfigCmd) + sizeof(struct LedsCfg *); 518 pConfigCmd = (struct ConfigCmd *)malloc(length); 519 if (!pConfigCmd) 520 return 1; 521 mLedsCfg.led_num = atoi(argv[3]); 522 mLedsCfg.value = atoi(argv[4]); 523 memcpy(pConfigCmd, &mConfigCmd, sizeof(mConfigCmd)); 524 memcpy(pConfigCmd->data, &mLedsCfg, sizeof(mLedsCfg)); 525 } else { 526 printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); 527 return 1; 528 } 529 } else if (strcmp(argv[1], "calibrate") == 0) { 530 if (argc != 3) { 531 printf("Wrong arg number\n"); 532 return 1; 533 } 534 mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; 535 mConfigCmd.rate = 0; 536 mConfigCmd.latency = 0; 537 mConfigCmd.cmd = CONFIG_CMD_CALIBRATE; 538 if (setType(&mConfigCmd, argv[2])) { 539 printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); 540 return 1; 541 } 542 } else if (strcmp(argv[1], "flush") == 0) { 543 if (argc != 3) { 544 printf("Wrong arg number\n"); 545 return 1; 546 } 547 mConfigCmd.evtType = EVT_NO_SENSOR_CONFIG_EVENT; 548 mConfigCmd.rate = 0; 549 mConfigCmd.latency = 0; 550 mConfigCmd.cmd = CONFIG_CMD_FLUSH; 551 if (setType(&mConfigCmd, argv[2])) { 552 printf("Unsupported sensor: %s For action: %s\n", argv[2], argv[1]); 553 return 1; 554 } 555 } else if (strcmp(argv[1], "load") == 0) { 556 if (argc != 3) { 557 printf("Wrong arg number\n"); 558 return 1; 559 } 560 561 return halCmd(NANOHUB_HAL_EXT_APPS_ON, argv[2]); 562 } else if (strcmp(argv[1], "unload") == 0) { 563 if (argc != 3) { 564 printf("Wrong arg number\n"); 565 return 1; 566 } 567 568 return halCmd(NANOHUB_HAL_EXT_APPS_OFF, argv[2]); 569 } else if (strcmp(argv[1], "delete") == 0) { 570 if (argc != 3) { 571 printf("Wrong arg number\n"); 572 return 1; 573 } 574 575 return halCmd(NANOHUB_HAL_EXT_APP_DELETE, argv[2]); 576 } else if (strcmp(argv[1], "download") == 0) { 577 int installCnt, uninstallCnt; 578 579 if (argc != 2) { 580 printf("Wrong arg number\n"); 581 return 1; 582 } 583 downloadNanohub(); 584 for (i = 0; i < MAX_DOWNLOAD_RETRIES; i++) { 585 int updateCnt = parseConfigAppInfo(&installCnt, &uninstallCnt); 586 if (updateCnt > 0) { 587 if (i == MAX_DOWNLOAD_RETRIES - 1) { 588 LOGE("Download failed after %d retries; erasing all apps " 589 "before final attempt", i); 590 eraseSharedArea(); 591 parseConfigAppInfo(&installCnt, &uninstallCnt); 592 } 593 removeApps(uninstallCnt); 594 downloadApps(installCnt); 595 resetHub(); 596 } else if (!updateCnt){ 597 return 0; 598 } 599 } 600 601 if (parseConfigAppInfo(&installCnt, &uninstallCnt) != 0) { 602 LOGE("Failed to download all apps!"); 603 return 1; 604 } 605 return 0; 606 } else { 607 printf("Unsupported action: %s\n", argv[1]); 608 return 1; 609 } 610 611 while (!fileWriteData("/dev/nanohub", pConfigCmd, length)) 612 continue; 613 614 if (pConfigCmd != &mConfigCmd) 615 free(pConfigCmd); 616 617 if (drain) { 618 signal(SIGINT, sig_handle); 619 fd = open("/dev/nanohub", O_RDONLY); 620 while (!stop) { 621 (void) read(fd, buf, buf_size); 622 } 623 close(fd); 624 } 625 return 0; 626 } 627