1/* check share_info in all partitions and update AppleVolumes.default and reload afpd. 2 * 3 * Copyright (C) 2008 - 2009, Delta Networks, Inc. 4 * 5 */ 6 7#include <stdio.h> 8#include <stdlib.h> 9#include <unistd.h> 10#include <errno.h> 11#include <string.h> 12#include <sys/time.h> 13#include <time.h> 14#include <signal.h> 15#include <ctype.h> 16 17/* 18 * The 'USB_Functionality_specification_v0.2.doc' is modified too much, so I don't want to 19 * touch the original code .... :) 20 */ 21#include <sys/types.h> 22#include <sys/stat.h> 23#include <sys/statfs.h> 24#include <unistd.h> 25#include <linux/magic.h> 26#include <sys/wait.h> 27 28#include "list.h" 29 30#if 0 31#define USB_DEBUGP(format, args...) printf(format, ## args) 32#else 33#define USB_DEBUGP(format, args...) 34#endif 35 36#define ITUNES_SHARE_FOLDER_SIZE 4096 37char itunes_share_floders[ITUNES_SHARE_FOLDER_SIZE] = {0}; 38char itunes_db_folder[128] = {0}; 39 40static const char daapd_conf[] = 41"general {\n" 42" uid = \"root\"\n" 43" logfile = \"%s/forked-daapd.log\"\n" 44" db_path = \"%s/forked-daapd.db\"\n" 45" loglevel = 5\n" 46" admin_password = \"unused\"\n" 47" ipv6 = false\n" 48"}\n" 49"library {\n" 50" name = \"%s\"\n" 51" port = 3689\n" 52" directories = { \"/tmp/itunes\", %s }\n" 53" remote_pairing_file = \"/tmp/itunes/apple.remote\"\n" 54" itunes_overrides = false\n" 55"}\n" 56"audio {\n" 57" nickname = \"R7800\"\n" 58"}"; 59 60struct disk_partition_info 61{ 62 struct list_head list; 63 int mounted; 64 int afplistupdated; 65 int ishfsplus; 66 char label; /* `U` ~ ... */ 67 char name[15]; /* `sda1` `sda2` */ 68 char vendor[128]; /*device name :SigmaTel MSCN*/ 69 char vol_name[31]; /* Volume Name */ 70 char device_id[128]; /* serialNum_partitionNum */ 71 unsigned long long capacity; /* capacity size in MB */ 72}; 73 74struct share_info 75{ 76 struct list_head list; 77 char name[]; 78}; 79 80#define USB_APPLE_VOLUMES_DEFAULT_CONF "/etc/netatalk/AppleVolumes.default" 81#define AVAHI_SERVICE_ADISK "/etc/avahi/services/adisk.service" 82#define TMP_AFP_LOCK "/tmp/tmp_afp_lock" 83#define SHARE_FILE_INFO "shared_usb_folder" 84#define APPROVED_DISK "USB_approved_device" 85 86#define USER_ADMIN "admin" 87#define USER_GUEST "guest" 88#define USB_PATH_SIZE 4096 89 90extern char *config_get(char* name); 91extern int config_match(char *name, char *match); 92 93char SATA_DEV_NAME[32]; 94char SATA_DEV_SERIAL_NO[128]; 95char SD_CARD_DEV_NAME[32]; 96 97static void reload_services(void) 98{ 99 int ret; 100 /* directly return ,when disable the usb network for afp access */ 101 /* FIXME: what is the use case??? */ 102// if (config_match("usb_enableNet", "1")) 103// return; 104 105 /* Sync with locking file, and wait 1s to not miss SIGUP for `afpd` */ 106 sleep(1); 107 ret = system("/bin/pidof afpd > /dev/zero 2>&1"); 108 if (ret != -1 && WIFEXITED(ret) && WEXITSTATUS(ret) == 0) { 109 system("/bin/kill -HUP `cat /var/run/afpd.pid` > /dev/null 2>&1"); 110 } else { 111 system("/bin/nice -n 19 /usr/sbin/afpd -F /etc/netatalk/afpd.conf -P /var/run/afpd.pid -c 7 > /dev/null 2>&1"); 112 } 113 114#if 0 /* Not required */ 115 /* cnid_metad */ 116 ret = system("/bin/pidof cnid_metad > /dev/zero 2>&1"); 117 if (ret != 0) 118 system("/usr/sbin/cnid_metad > /dev/zero 2>&1"); 119#endif 120 121 /* avahi-daemon: not required */ 122} 123 124static inline char *user_name(char *code) 125{ 126 if (*code == '1') 127 return USER_ADMIN; 128 else 129 return USER_GUEST; 130} 131 132static void add_afpd_share_info(FILE *fp, char *displayname, char *reader, char *writer, char *path) 133{ 134 fprintf(fp, "%s \"%s\"", path, displayname); 135 136 /* FIXME: set proper permission and/or allow proper user */ 137 if (strncmp(reader, USER_GUEST, strlen(USER_GUEST))) 138 fprintf(fp, " allow:@admin deny:@guest"); 139 else if (strncmp(writer, USER_GUEST, strlen(USER_GUEST))) 140 fprintf(fp, " allow:@admin,@guest rolist:@guest"); 141 else 142 fprintf(fp, " allow:@admin,@guest"); 143 144 fprintf(fp, " cnidscheme:cdb options:usedots,tm\n"); 145} 146 147int is_sda(char * dev) 148{ 149 int count = 0; 150 FILE *fp; 151 char part_name[16], line[128]; 152 int major, minors; 153 unsigned long long capacity; 154 155 fp = fopen("/proc/partitions", "r"); 156 if (fp == NULL) 157 goto ret; 158 159 /* 160 * * major minor #blocks name 161 * * 162 * * 31 0 320 mtdblock0 163 * * .... 164 * * 8 0 3968000 sda 165 * * 8 1 3963968 sda1 166 * 167 */ 168 169 while (fgets(line, sizeof(line), fp)) { 170 if (sscanf(line, " %d %d %llu %[^\n ]", 171 &major, &minors, &capacity, part_name) != 4) 172 continue; 173 if (strncmp(part_name, dev, 3)) 174 continue; 175 else 176 count++; 177 } 178 179ret: 180 if (fp != NULL) 181 fclose(fp); 182 183 return ((count == 1)?1:0); 184} 185 186static char *get_device_vendor(char *dev) 187{ 188 int i,j; 189 FILE *fp; 190 char line[100]; 191 static char vendor[128]; 192 char path[64], *ven_mod[] = {"vendor", "model"}; 193 194 vendor[0] = '\0'; 195 196 for (i=0; i<2; i++){ 197 snprintf(path, sizeof(path), "/sys/block/%s/device/%s", dev, ven_mod[i]); 198 if (!(fp = fopen(path, "r"))) 199 continue; 200 fgets(line, sizeof(line), fp); 201 fclose(fp); 202 203 j = 0; 204 while (line[j] != '\0' && line[j] != '\r' && line[j] != '\n') 205 j++; 206 line[j] = '\0'; 207 208 strcat(vendor, line); 209 strcat(vendor, " "); 210 } 211 212 j = 127; 213 while(vendor[j] == '\t' || vendor[j] == ' ' || vendor[j] == '\0') 214 j--; 215 vendor[j + 1] = '\0'; 216 217 return vendor; 218} 219 220/* 221 * When presenting the Capacity of a device, the appropriate units should be used. 222 * If a device is 1GB in size this should be displayed as 1GB, however a 300MB device 223 * should be displayed as 300MB and not 0.3GB. (29.66GB, 44.53GB). 224 */ 225static void format_capacity(char *buf, int buflen, unsigned long long megabytes) 226{ 227 if (megabytes >= 1024) { 228 unsigned long long left = ((megabytes & 0x3FF) * 100) >> 10; // (leftMB / 1024) * 100 229 if (left == 0) 230 snprintf(buf, buflen, "%llu GB", (megabytes >> 10)); 231 else 232 snprintf(buf, buflen, "%llu.%02llu GB", (megabytes >> 10), left); 233 } else { 234 snprintf(buf, buflen, "%llu MB", megabytes); 235 } 236} 237 238static int is_special_backup_format(char *dev) 239{ 240#define DISK_FORMAT_FILE "/tmp/disk_special_format" 241 int ret = 0; 242 int i = 0; 243 FILE *fp; 244 char cmd[128], buf[256]; 245 246 memset(cmd,0,128); 247 memset(buf,0,256); 248 snprintf(cmd, sizeof(cmd), "/bin/mount | grep '/mnt/%s' | awk '{print$5}' > " DISK_FORMAT_FILE, dev); 249 system(cmd); 250 251 if (!(fp = fopen(DISK_FORMAT_FILE, "r"))) 252 return -1; 253 254 fgets(buf, sizeof(buf), fp); 255 fclose(fp); 256 257 i = 0; 258 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 259 i++; 260 buf[i] = '\0'; 261 if ( !strcmp(buf,"hfsplus") || !strcmp(buf, "xfs") || !strcmp(buf, "ntfs") 262 || !strcmp(buf, "ext2") || !strcmp(buf, "ext3") || !strcmp(buf, "ext4") ) 263 ret = 1; 264 //printf("dev = %s, buf = %s", dev, buf); 265 return ret; 266} 267 268static int is_hfsplus_formated(char *dev) 269{ 270#define VOLUME_FORMAT_FILE "/tmp/disk_format" 271 int ret = 0; 272 int i = 0; 273 char cmd[128], buf[256]; 274 FILE *fp; 275 276 memset(cmd,0,128); 277 memset(buf,0,256); 278 279 snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -t /dev/%s > " VOLUME_FORMAT_FILE, dev); 280 system(cmd); 281 282 if (!(fp = fopen(VOLUME_FORMAT_FILE, "r"))) { 283 return -1; 284 } 285 286 fgets(buf, sizeof(buf), fp); 287 fclose(fp); 288 289 i = 0; 290 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 291 i++; 292 buf[i] = '\0'; 293 294 if ( !strcmp(buf,"hfsplus") || !strcmp(buf, "xfs") || !strcmp(buf, "ntfs") 295 || !strcmp(buf, "ext2") || !strcmp(buf, "ext3") || !strcmp(buf, "ext4") ) 296 ret = 1; 297 298 USB_DEBUGP("\nDev: %s vol_type: %s ret: %d\n", dev, buf, ret); 299 return ret; 300} 301 302static char * get_usb_serial_num(char *part_name) 303{ 304 static char serial_num[128]; 305 char disk_name[8], path[64], line[128], cmd[256]; 306 char *bus_num = NULL; 307 char *p; 308 int j = 0; 309 FILE *fp; 310 311 serial_num[0] = '\0'; 312 313 snprintf(disk_name, 8, "%s", part_name); 314 disk_name[3] = '\0'; 315 sprintf(cmd, "/bin/ls /sys/block/%s/device/scsi_device/ > /tmp/%s_info_tmp", disk_name, disk_name); 316 system(cmd); 317 318 sprintf(path, "/tmp/%s_info_tmp", disk_name); 319 fp= fopen(path, "r"); 320 if (fp == NULL) 321 goto ret; 322 while (fgets(line, sizeof(line), fp)) { 323 line[15] = '\0'; 324 char *tok = ":\n"; 325 bus_num = strtok(line, tok); 326 fclose(fp); 327 } 328 329 if(bus_num != NULL){ 330 snprintf(path, 64, "/proc/scsi/usb-storage/%s", bus_num); 331 fp= fopen(path, "r"); 332 if (fp == NULL) 333 goto ret; 334 335 while (fgets(line, sizeof(line), fp)) { 336 if(strncmp(line, "Serial Number:", 14) == 0){ 337 while (line[j] != '\r' && line[j] != '\n') 338 j++; 339 line[j] = '\0'; 340 341 /* delete the space/tab at the end of the Serial Number */ 342 p = line + 14; 343 while (*p != '\0') 344 ++p; 345 --p; 346 while (*p == ' ' || *p == '\t') 347 --p; 348 *(p + 1) = '\0'; 349 /* skip the space/tab at the head of the Serial Number */ 350 p = line + 14; 351 while (*p == ' ' || *p == '\t') 352 p++; 353 strcpy(serial_num, p); 354 } 355 } 356 } 357 358ret: 359 if (fp != NULL) 360 fclose(fp); 361 return serial_num; 362} 363 364static char * get_sata_serial_num(void) 365{ 366 return SATA_DEV_SERIAL_NO; 367} 368 369/* use filesystem uuid instead serial num of sd card */ 370static char * get_sd_card_serial_num(char *part_name) 371{ 372#define SD_CARD_UUID_FILE "/tmp/sd_card_uuid" 373 static char uuid[128]; 374 char buf[128], cmd[64]; 375 int i = 0; 376 FILE *fp; 377 buf[0] = '\0'; 378 379 snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -u /dev/%s > "SD_CARD_UUID_FILE, part_name); 380 system(cmd); 381 if(!(fp = fopen(SD_CARD_UUID_FILE, "r"))) 382 return get_usb_serial_num(part_name); 383 384 fgets(buf, sizeof(buf), fp); 385 fclose(fp); 386 387 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 388 i++; 389 buf[i] = '\0'; 390 if (i == 0) 391 return get_usb_serial_num(part_name); 392 strncpy(uuid, buf, sizeof(uuid)); 393 return uuid; 394} 395 396static void get_device_id(struct disk_partition_info *disk) 397{ 398 char *id; 399 id = disk->device_id; 400 if(strncmp(disk->name, SATA_DEV_NAME, 3) == 0) 401 snprintf(id, sizeof(disk->device_id), "%s*%s", get_sata_serial_num(), disk->name + 3); 402 else if(strncmp(disk->name, SD_CARD_DEV_NAME, 3) == 0) 403 snprintf(id, sizeof(disk->device_id), "%s*%s", get_sd_card_serial_num(disk->name), disk->name + 3); 404 else 405 snprintf(id, sizeof(disk->device_id), "%s*%s", get_usb_serial_num(disk->name), disk->name + 3); 406 407} 408 409static void get_disk_volume(struct disk_partition_info *disk, char *part_name) 410{ 411//#define VOLUME_ID_FILE "/tmp/vol_id" 412/* FIX Bug 28761 - [USB]sometimes some drives appear as "Not Shared" 413 cause: 414 Module net-cgi and samba-script will read and write the 415 same file, sometimes it will cause conflict. 416 solution: 417 Use two tmp file to avoid the conflict. 418 Module net-cgi use "vol_id" 419 Module samba-script use "vol_id_1" 420*/ 421#define VOLUME_ID_FILE "/tmp/vol_id_2" 422 int i; 423 FILE *fp; 424 char *buf, disknum[4], diskname[4], capacity[32], cmd[256]; 425 426 buf = disk->vol_name; 427 buf[0] = '\0'; 428 429 snprintf(cmd, sizeof(cmd), "/usr/sbin/vol_id -L /dev/%s > " VOLUME_ID_FILE, part_name); 430 system(cmd); 431 432 if(!(fp = fopen(VOLUME_ID_FILE, "r"))){ 433 printf("[get_disk_volume vol_id] open file vol_id_2 error!!\n"); 434 fclose(fp); 435 goto ret; 436 } 437 438 fgets(buf, sizeof(disk->vol_name), fp); 439 fclose(fp); 440 441 i = 0; 442 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 443 i++; 444 buf[i] = '\0'; 445 446 if (buf[0] == '\0'){ 447 //printf("[afp: get_disk_volume vol_id] Get info by vol_id failed!!\n"); 448 memset(cmd,0,sizeof(cmd)); 449 strncpy(diskname, part_name, 3); 450 diskname[3] = '\0'; 451 disknum[0] = part_name[3]; 452 disknum[1] = '\0'; 453 snprintf(cmd, sizeof(cmd), "/bin/echo $\(/usr/sbin/parted -s /dev/%s print | grep \"Number\" -A16 | sed -n '2,16p' | awk 'NF>=6{for(n=6;n<=NF;n++)printf $n\" \";print \"\"}' | sed -n %dp) > " VOLUME_ID_FILE, diskname, atoi(disknum)); 454 system(cmd); 455 if(!(fp = fopen(VOLUME_ID_FILE, "r"))){ 456 printf("[get_disk_volume parted] open file vol_id_2 error!!\n"); 457 fclose(fp); 458 goto ret; 459 } 460 fgets(buf, sizeof(disk->vol_name), fp); 461 fclose(fp); 462 463 i = 0; 464 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 465 i++; 466 buf[i] = '\0'; 467 } 468 469ret: 470 /* 471 * If Volume Name is empty, then use <USB Device Letter> Drive (<Capacity>) 472 * e.g U Drive (512MB) 473 */ 474 475 format_capacity(capacity, sizeof(capacity), disk->capacity); 476 if (buf[0] == '\0') { 477 printf("[vol_id_2]get_disk_volume error, goto ret!!!\n"); 478 if(strncmp(part_name, SATA_DEV_NAME, 3) == 0) { 479 snprintf(buf, sizeof(disk->vol_name), "%c External_Disk(%s)", disk->label, capacity); 480 }else if(strncmp(part_name, SD_CARD_DEV_NAME, 3) == 0){ 481 snprintf(buf, sizeof(disk->vol_name), "%c Sd_Card (%s)", disk->label, capacity); 482 }else{ 483 snprintf(buf, sizeof(disk->vol_name), "%c Drive (%s)", disk->label, capacity); 484 } 485 } 486} 487 488static int is_loop_partition(char *dev) 489{ 490#define DISK_LOOP_PARTITION "/tmp/disk_loop_pt" 491 int ret = 0; 492 int i = 0; 493 char diskname[4], cmd[128], buf[256]; 494 FILE *fp; 495 496 memset(cmd,0,128); 497 memset(buf,0,256); 498 499 strncpy(diskname, dev, 3); 500 diskname[3] = '\0'; 501 snprintf(cmd, sizeof(cmd), "/usr/sbin/parted -s /dev/%s print | grep \"Partition Table\" | awk '{print $3}' > " DISK_LOOP_PARTITION, diskname); 502 system(cmd); 503 if (!(fp = fopen(DISK_LOOP_PARTITION, "r"))) { 504 fclose(fp); 505 return 0; 506 } 507 fgets(buf, sizeof(buf), fp); 508 fclose(fp); 509 510 i = 0; 511 while (buf[i] != '\0' && buf[i] != '\r' && buf[i] != '\n') 512 i++; 513 buf[i] = '\0'; 514 515 if ( !strcmp(buf,"loop") ){ 516 fprintf(stderr, "[is_loop_partition]: Disk %s is a loop partition!\n", dev); 517 ret = 1; 518 } 519 520 return ret; 521} 522 523static int is_noshare_partition(char *dev) 524{ 525 FILE *fp; 526 char diskname[4], cmd[128], result[8]; 527 528 if (strlen(dev) != 4) 529 return 0; 530 531 strncpy(diskname, dev, 3); 532 diskname[3] = '\0'; 533 534 snprintf(cmd, sizeof(cmd), "/usr/sbin/parted -s /dev/%s print noshare | grep %s", diskname, dev); 535 fp = popen(cmd, "r"); 536 if (!fp) { 537 perror("popen"); 538 return 0; 539 } 540 541 memset(result, 0, sizeof(result)); 542 fgets(result, sizeof(result), fp); 543 544 pclose(fp); 545 546 return strlen(result) >= 4 ? 1 : 0; 547} 548 549static void scan_disk_entries(struct list_head *head) 550{ 551 FILE *fp; 552 struct statfs statbuf; 553 int i = 0, j=0, k = 0, major,minors; 554 int have_disk_mouted = 0, have_hfsplus_disk_mounted = 0; 555 unsigned long long capacity; 556 char mnt_path[32],*vendor = NULL; 557 char *s, part_name[128], line[256]; 558 struct disk_partition_info *partinfo; 559 560 fp = fopen("/proc/partitions","r"); 561 if (fp == NULL ) 562 return; 563 564 /* 565 * major minor #blocks name 566 * 567 * 31 0 320 mtdblock0 568 * .... 569 * 8 0 3968000 sda 570 * 8 1 3963968 sda1 571 */ 572 while (fgets(line,sizeof(line),fp)) { 573 if (sscanf(line, " %d %d %llu %[^\n ]", 574 &major, &minors, &capacity, part_name) != 4) 575 continue; 576 if (strncmp(part_name, "sd", 2)) 577 continue; 578 for (s = part_name; *s; s++) 579 ; 580 if (!isdigit(s[-1])) { 581 vendor = get_device_vendor(part_name); 582 583 if (!is_sda(part_name)) 584 continue; 585 } 586 587 capacity >>= 10; /* unit: 1KB .. >> 1 size /512 (long *arg) */ 588 if (capacity == 0) 589 continue; /*It indicates that this partition should be an extended partition. */ 590 591 if (!is_loop_partition(part_name)){ 592 if (is_noshare_partition(part_name)) 593 continue; 594 } 595 596 partinfo = malloc(sizeof(struct disk_partition_info)); 597 if (partinfo == NULL) 598 continue; 599 600 /* SEE: hotplug2.mount ==> mount /dev/$1 /mnt/$1 */ 601 snprintf(mnt_path, sizeof(mnt_path), "/mnt/%s", part_name); 602 /* NO Disk, the mount point directory is NOT removed, this magic value is `0x858458F6` */ 603 if (statfs(mnt_path, &statbuf) == 0 && (unsigned int)statbuf.f_type != 0x858458F6 && (unsigned int)statbuf.f_type != TMPFS_MAGIC) 604 partinfo->mounted = 1, have_disk_mouted = 1; 605 else 606 partinfo->mounted = 0; 607 partinfo->afplistupdated = 0; 608 //partinfo->ishfsplus = is_hfsplus_formated(part_name); 609 if ( (is_hfsplus_formated(part_name)) || (is_special_backup_format(part_name)) ){ 610 printf("This HDD format can support the feature of Time Machine... ...\n"); 611 partinfo->ishfsplus = 1; 612 }else{ 613 printf("[Waring]: This HDD format failed to support the Time Machine!!!!!!\n"); 614 partinfo->ishfsplus = 0; 615 } 616 if (partinfo->ishfsplus == 1) 617 have_hfsplus_disk_mounted = 1; 618 partinfo->capacity = capacity; 619 if(strncmp(part_name, SATA_DEV_NAME, 3) == 0){ 620 partinfo->label = 's' - j; 621 j++; 622 }else if(strncmp(part_name, SD_CARD_DEV_NAME, 3) == 0){ 623 partinfo->label = '0' + k; 624 k++; 625 }else{ 626 partinfo->label = 'U' - i; 627 i++; 628 } 629 snprintf(partinfo->name, sizeof(partinfo->name),"%s", part_name); 630 if (vendor) 631 strcpy(partinfo->vendor,vendor); 632 633 get_device_id(partinfo); 634 get_disk_volume(partinfo, part_name); 635 636 list_add_tail(&partinfo->list, head); 637 638 USB_DEBUGP("[USB-AFP]: Found partition %s, mounted %s!!!\n", part_name, 639 partinfo->mounted ? "Yes" : "No"); 640 } 641 642 fclose(fp); 643 USB_DEBUGP("[USB-AFP]: Total %d partitions are FOUND!\n", i); 644 645 if (have_disk_mouted) { 646 /* reload avahi with afpd and adisk services */ 647 if (have_hfsplus_disk_mounted) 648 system("cp /usr/config/avahi/services/afpd.service /etc/avahi/services/ > /dev/null 2>&1"); 649 else 650 system("rm /etc/avahi/services/afpd.service > /dev/null 2>&1"); 651 system("cp /usr/config/avahi/services/adisk.service /etc/avahi/services/ > /dev/null 2>&1"); 652 system("echo \" <txt-record>sys=waMA=$(/bin/config get wan_factory_mac),adVF=0x1000</txt-record>\" >> /etc/avahi/services/adisk.service"); 653 } else { 654 /* reload avahi without afpd and adisk services */ 655 system("rm /etc/avahi/services/afpd.service > /dev/null 2>&1"); 656 system("rm /etc/avahi/services/adisk.service > /dev/null 2>&1"); 657 } 658} 659 660static inline int duplicate_share_name(char *name, struct list_head *head) 661{ 662 struct list_head *pos; 663 struct share_info *share; 664 665 list_for_each(pos, head) { 666 share = list_entry(pos, struct share_info, list); 667 if (strcmp(share->name, name) == 0) 668 return 1; 669 } 670 671 return 0; 672} 673 674static inline void add_share_info_list(char *name, struct list_head *head) 675{ 676 struct share_info *share; 677 678 share = malloc(sizeof(struct share_info) + strlen(name) + 1); 679 if (share == NULL) 680 return; 681 strcpy(share->name, name); 682 list_add_tail(&share->list, head); 683} 684 685/* encode string to xml-string */ 686static char *xml_encode(char *share_name) 687{ 688 int i = 0; 689 int output_len = 0; 690 int temp_expansion_len = 0; 691 char *encoded_xml_string = NULL; 692 char temp_expansion[10]; 693 char *p1 = NULL; 694 695 p1 = share_name; 696 output_len = (int)strlen(share_name) * 6; 697 698 encoded_xml_string = (char *) malloc(output_len + 1); 699 if (encoded_xml_string == NULL) 700 return NULL; 701 702 while (*p1) { 703 /* alpha-numeric characters don't get encoded */ 704 if ((*p1 >= '0' && *p1 <= '9') || (*p1 >= 'A' && *p1 <= 'Z') || (*p1 >= 'a' && *p1 <= 'z')) { 705 encoded_xml_string[i++] = *p1; 706 707 /* spaces, hyphens, periods, underscores and colons don't get encoded */ 708 } else if ((*p1 == ' ') || (*p1 == '-') || (*p1 == '.') || (*p1 == '_') || (*p1 == ':')) { 709 encoded_xml_string[i++] = *p1; 710 711 /* ',' char encoded as "\," */ 712 } else if (*p1 == ',') { 713 if (i < (output_len - 2)) { 714 strcpy(&encoded_xml_string[i], "\\,"); 715 i += 2; 716 } 717 718 /* for simplicity, all other chars represented by their numeric value */ 719 } else { 720 snprintf(temp_expansion, 9, "&#%d;", (unsigned char)(*p1)); 721 temp_expansion_len = (int)strlen(temp_expansion); 722 if (i < (output_len - temp_expansion_len)) { 723 strcpy(&encoded_xml_string[i], temp_expansion); 724 i += temp_expansion_len; 725 } 726 } 727 p1++; 728 } 729 730 encoded_xml_string[i] = '\0'; 731 return encoded_xml_string; 732} 733 734static int update_adisk(int file_fmt, char *share_name) 735{ 736 static int cnt = 0; 737 FILE *adisk_conf_fp = NULL; 738 char *encoded_share_name = NULL; 739 740 if((adisk_conf_fp = fopen(AVAHI_SERVICE_ADISK, "a") ) == NULL) { 741 USB_DEBUGP("[USB-AFP]: Unable To Open Adisk Service File.....\n"); 742 return -1; 743 } 744 745 fseek(adisk_conf_fp, 0, SEEK_END); 746 747 encoded_share_name = xml_encode(share_name); 748 if (file_fmt == 1) { 749 /* For HFS+ File System that will be shown in the TimeMachine available disk list */ 750 fprintf(adisk_conf_fp, " <txt-record>dk%d=adVF=0x1003,adVN=%s,adVU=</txt-record>\n", cnt++, (encoded_share_name ? encoded_share_name : share_name)); 751 } else { 752 /* For other file system that will be not appear in TimeMachine disk list, 753 * but can be viewed in Finder window */ 754 fprintf(adisk_conf_fp, " <txt-record>dk%d=adVF=0x1002,adVN=%s,adVU=</txt-record>\n", cnt++, (encoded_share_name ? encoded_share_name : share_name)); 755 } 756 757 if (encoded_share_name) 758 free(encoded_share_name); 759 760 fclose(adisk_conf_fp); 761 return 0; 762} 763 764static void commit_adisk() 765{ 766 char cmd[256]; 767 768 memset(cmd,0,256); 769 sprintf(cmd,"echo -e \" </service>\n</service-group>\" >> %s", AVAHI_SERVICE_ADISK); 770 system(cmd); 771} 772 773static int check_approved_disk(struct disk_partition_info *diskinfo) 774{ 775 int i = 0, len = 0; 776 char *p; 777 char approved_info[32], value[512], device_id[128]; 778 779 if(config_match("usb_enableUSB", "0")) 780 return 0; 781 782 strncpy(device_id, diskinfo->device_id, 128); 783 for( ; device_id[i] != '*'; i++); 784 device_id[i] = '\0'; 785 786 i = 1; 787 for (; ; i++) { 788 sprintf(approved_info, "%s%d", APPROVED_DISK, i); 789 len = sprintf(value, "%s", config_get(approved_info)); 790 if (len < 1) 791 break; 792 p = value + len; 793 for( ; *p != '*'; p--); 794 p++; 795 796 if (strcmp(device_id, p) == 0) 797 return 0; 798 } 799 return 1; 800} 801 802static void load_share_info(FILE *fp, char *diskname) 803{ 804 int no_shareinfo = 1; /* If `diskname` is not NULL, check if there is no share info in disk */ 805 int num_mounted_disk = 0; 806 807 struct share_info *shareinfo; 808 struct disk_partition_info *diskinfo; 809 struct list_head disk_lists, share_lists, *pos, *nxt; 810 811 INIT_LIST_HEAD(&disk_lists); 812 INIT_LIST_HEAD(&share_lists); 813 814 scan_disk_entries(&disk_lists); 815 816 USB_DEBUGP("[USB-AFP]: Loading USB share information ......diskname: %s\n", diskname); 817 list_for_each(pos, &disk_lists) { 818 int j; 819 char name[64], device_id[128], oneline[1024]; 820 char *val, *volumeName, *deviceName, *serial_num, *partition_id; 821 char fullpath[USB_PATH_SIZE],dupshare[USB_PATH_SIZE]; 822 char *sep, *t_share_name, *folderName, *readAccess, *writeAccess; 823 char share_name[128]; 824 825 diskinfo = list_entry(pos, struct disk_partition_info, list); 826 if (!diskinfo->mounted) 827 continue; 828 if (check_approved_disk(diskinfo)){ 829 no_shareinfo = 0; 830 continue; 831 } 832 833 sep = "*\n"; 834 for (j=0; ;j++) { 835 sprintf(name, SHARE_FILE_INFO"%d",j); 836 val = config_get(name); 837 if (*val == '\0') 838 break; 839 840 strcpy(oneline, val); 841 842 t_share_name = strtok(oneline, sep); /* share name */ 843 folderName = strtok(NULL, sep); /* folder name */ 844 readAccess = strtok(NULL, sep); /* readAccess*/ 845 writeAccess = strtok(NULL, sep); /* writeAccess */ 846 volumeName = strtok(NULL, sep); /* volumeName*/ 847 deviceName = strtok(NULL, sep); /* deviceName */ 848 serial_num = strtok(NULL, sep); /* serialNum */ 849 partition_id = strtok(NULL, sep); /* partitionNum */ 850 851 memset(share_name, 0, 128); 852 sprintf(share_name, "%s", t_share_name); 853 854 if (share_name == NULL || folderName == NULL || readAccess == NULL ||writeAccess == NULL || 855 volumeName == NULL || deviceName == NULL ) 856 continue; 857 858 snprintf(device_id, sizeof(device_id), "%s*%s", serial_num, partition_id); 859 if(strcmp(device_id,diskinfo->device_id) || strcmp(volumeName,diskinfo->vol_name) || strcmp(deviceName,diskinfo->vendor)) 860 continue; 861 862 if (duplicate_share_name(share_name, &share_lists)) { 863 // Fixme: if volume name also different then dupplicate. 864 snprintf(dupshare, sizeof(dupshare), "%s(%c)", share_name, diskinfo->label); 865 memset(share_name, 0, 128); 866 sprintf(share_name,"%s", dupshare); 867 } 868 869 add_share_info_list(share_name, &share_lists); 870 871 snprintf(fullpath, sizeof(fullpath), "/mnt/%s%s", diskinfo->name, folderName); 872 873 readAccess = user_name(readAccess); 874 writeAccess = user_name(writeAccess); 875 876 USB_DEBUGP("[USB-AFP]: AFPInfo %s Folder:%s Reader:%s Writer: %s\n", share_name, folderName, readAccess, writeAccess); 877 878 if (diskinfo->ishfsplus) { 879 add_afpd_share_info(fp, share_name, readAccess, writeAccess, fullpath); 880 update_adisk(1,share_name); 881 } else { 882 update_adisk(0,share_name); 883 } 884 885 if (itunes_db_folder[0] == 0) 886 sprintf(itunes_db_folder, "/tmp/mnt/%s/.itunes", diskinfo->name); 887 if (strcmp(readAccess, USER_GUEST) == 0) { 888 int len = strlen(itunes_share_floders); 889 if (len == 0) 890 sprintf(itunes_share_floders, "\"%s\"", fullpath); 891 else if(len + strlen(fullpath) + 5 < ITUNES_SHARE_FOLDER_SIZE) 892 sprintf(itunes_share_floders + len, ", \"%s\"", fullpath); 893 else 894 printf("Warning: %s is not added in itunes_share_floders\n", fullpath); 895 } 896 897 diskinfo->afplistupdated = 1; 898 num_mounted_disk++; 899 USB_DEBUGP("\nIn First Step: valume: %s afplist: %d", diskinfo->vol_name, diskinfo->afplistupdated); 900 901 if (diskname != NULL && strncmp(diskinfo->name, diskname, 3) == 0) 902 no_shareinfo = 0; 903 } 904 } 905 906 list_for_each_safe(pos, nxt, &disk_lists) { 907 diskinfo = list_entry(pos, struct disk_partition_info, list); 908 list_del(pos); 909 free(diskinfo); 910 } 911 912 list_for_each_safe(pos, nxt, &share_lists) { 913 shareinfo = list_entry(pos, struct share_info, list); 914 list_del(pos); 915 free(shareinfo); 916 } 917 918 if (num_mounted_disk > 0) { 919 commit_adisk(); 920 } 921} 922 923void cleanup(int signal) 924{ 925 printf("Try to recover from endless waiting.\n"); 926 reload_services(); 927 unlink(TMP_AFP_LOCK); 928 exit(1); 929} 930 931int check_afp_locked(void) 932{ 933 return (!access(TMP_AFP_LOCK, F_OK)); 934} 935 936void check_sata_dev(void) 937{ 938 strcpy(SATA_DEV_NAME, config_get("sata_diskname")); 939 strcpy(SATA_DEV_SERIAL_NO, config_get("sata_serial_no")); 940} 941 942void check_sd_card_dev(void) 943{ 944 strcpy(SD_CARD_DEV_NAME, config_get("sd_card_diskname")); 945} 946 947 948int main(int argc, char**argv) 949{ 950 FILE *fp, *filp; 951 char *diskname = NULL; 952 char *device_name; 953 struct timeval currenttime, newtime; 954 955 signal(SIGINT, cleanup); 956 signal(SIGTERM, cleanup); 957 958 gettimeofday(¤ttime, NULL); 959 960 while (check_afp_locked()) { 961 gettimeofday(&newtime, NULL); 962 /* the longest waiting time is 30s, avoid endless waiting */ 963 if ((newtime.tv_sec - currenttime.tv_sec) > 30) 964 cleanup(0); 965 sleep(1); 966 } 967 968 /* create lock file */ 969 filp = fopen(TMP_AFP_LOCK, "w+"); 970 if (filp) 971 fclose(filp); 972 else { 973 perror("error when creating afp_lock file!\n"); 974 return 1; 975 } 976 977 check_sata_dev(); 978 check_sd_card_dev(); 979 980 fp = fopen(USB_APPLE_VOLUMES_DEFAULT_CONF, "w"); 981 if (fp == NULL) 982 goto unlock; 983 984 if (argc == 2 && strlen(argv[1]) == 3 && strncmp(argv[1], "sd", 2) == 0) 985 diskname = argv[1]; /* sd[a-z] */ 986 987 load_share_info(fp, diskname); 988 989 fclose(fp); 990 991 reload_services(); 992 993 system("/etc/init.d/forked-daapd stop"); 994 if (config_match("endis_itunes", "1") && itunes_share_floders[0] != 0 && (fp = fopen("/etc/forked-daapd.conf", "w")) != NULL) { 995 device_name = config_get("upnp_serverName"); 996 if (*device_name != '\0') 997 fprintf(fp, daapd_conf, itunes_db_folder, itunes_db_folder, config_get("upnp_serverName"), itunes_share_floders); 998 else 999 fprintf(fp, daapd_conf, itunes_db_folder, itunes_db_folder, config_get("Device_name"), itunes_share_floders); 1000 fclose(fp); 1001 char filename[256]; 1002 1003 if (access(itunes_db_folder, 0)) 1004 mkdir(itunes_db_folder, 0777); 1005 else { 1006 sprintf(filename, "%s/forked-daapd.log", itunes_db_folder); 1007 unlink(filename); 1008 sprintf(filename, "%s/forked-daapd.db", itunes_db_folder); 1009 unlink(filename); 1010 } 1011 system("/etc/init.d/forked-daapd start"); 1012 } 1013unlock: 1014 unlink(TMP_AFP_LOCK); 1015 return 0; 1016} 1017