|
32 | 32 | #include <sys/types.h> |
33 | 33 | #include <sys/stat.h> |
34 | 34 | #include <sys/time.h> |
| 35 | +#include <sys/wait.h> |
35 | 36 | #include <sys/xattr.h> |
36 | 37 | #include <fcntl.h> |
37 | 38 | #include <curl/curl.h> |
@@ -150,6 +151,10 @@ typedef struct { |
150 | 151 | FILE *f; /*!< |
151 | 152 | fdopened file descriptor from LrDownloadTarget and used |
152 | 153 | in curl_handle. */ |
| 154 | + FILE *writef; /*!< |
| 155 | + the fd to write data to. Could be a subprocess. */ |
| 156 | + pid_t pid; /*!< |
| 157 | + the pid of a transcoder. */ |
153 | 158 | char errorbuffer[CURL_ERROR_SIZE]; /*!< |
154 | 159 | Error buffer used in curl handle */ |
155 | 160 | GSList *tried_mirrors; /*!< |
@@ -613,7 +618,7 @@ lr_writecb(char *ptr, size_t size, size_t nmemb, void *userdata) |
613 | 618 | if (range_start <= 0 && range_end <= 0) { |
614 | 619 | // Write everything curl give to you |
615 | 620 | target->writecb_recieved += all; |
616 | | - return fwrite(ptr, size, nmemb, target->f); |
| 621 | + return fwrite(ptr, size, nmemb, target->writef); |
617 | 622 | } |
618 | 623 |
|
619 | 624 | /* Deal with situation when user wants only specific byte range of the |
@@ -1443,6 +1448,132 @@ open_target_file(LrTarget *target, GError **err) |
1443 | 1448 | return f; |
1444 | 1449 | } |
1445 | 1450 |
|
| 1451 | +/** Maybe transcode the file |
| 1452 | + */ |
| 1453 | +void |
| 1454 | +maybe_transcode(LrTarget *target, GError **err) |
| 1455 | +{ |
| 1456 | + const char *e = g_getenv("LIBREPO_TRANSCODE_RPMS"); |
| 1457 | + int transcoder_stdin[2], fd; |
| 1458 | + pid_t pid; |
| 1459 | + FILE *out; |
| 1460 | + _cleanup_strv_free_ gchar **args = NULL; |
| 1461 | + target->writef = NULL; |
| 1462 | + if (!e) { |
| 1463 | + g_debug("Not transcoding"); |
| 1464 | + target->writef = target->f; |
| 1465 | + return; |
| 1466 | + } |
| 1467 | + if (g_str_has_suffix(target->target->path, ".rpm") == FALSE) { |
| 1468 | + g_debug("Not transcoding %s due to name", target->target->path); |
| 1469 | + target->writef = target->f; |
| 1470 | + return; |
| 1471 | + } |
| 1472 | + g_debug("Transcoding %s", target->target->path); |
| 1473 | + args = g_strsplit(e, " ", -1); |
| 1474 | + if (args[0] == NULL) { |
| 1475 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1476 | + "transcode env empty"); |
| 1477 | + return; |
| 1478 | + } |
| 1479 | + if (pipe(transcoder_stdin) != 0) { |
| 1480 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1481 | + "input pipe creation failed: %s", |
| 1482 | + g_strerror(errno)); |
| 1483 | + return; |
| 1484 | + } |
| 1485 | + if (fcntl(transcoder_stdin[1], F_SETFD, FD_CLOEXEC) != 0) { |
| 1486 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1487 | + "input pipe write close-on-fork failed: %s", |
| 1488 | + g_strerror(errno)); |
| 1489 | + return; |
| 1490 | + } |
| 1491 | + pid = fork(); |
| 1492 | + if (pid == -1) { |
| 1493 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1494 | + "fork failed: %s", |
| 1495 | + g_strerror(errno)); |
| 1496 | + return; |
| 1497 | + } |
| 1498 | + if (pid == 0) { |
| 1499 | + /* child */ |
| 1500 | + if (dup2(transcoder_stdin[0], STDIN_FILENO) == -1) { |
| 1501 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1502 | + "dup2 of stdin failed: %s", |
| 1503 | + g_strerror(errno)); |
| 1504 | + return; |
| 1505 | + } |
| 1506 | + close(transcoder_stdin[0]); |
| 1507 | + close(transcoder_stdin[1]); |
| 1508 | + fd = fileno(target->f); |
| 1509 | + if (fd == -1) { |
| 1510 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1511 | + "fileno for target failed"); |
| 1512 | + return; |
| 1513 | + } |
| 1514 | + if (dup2(fd, STDOUT_FILENO) == -1) { |
| 1515 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1516 | + "dup2 of stdout failed: %s", |
| 1517 | + g_strerror(errno)); |
| 1518 | + return; |
| 1519 | + } |
| 1520 | + if (execv(args[0], args) == -1) { |
| 1521 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1522 | + "execv failed: %s", g_strerror(errno)); |
| 1523 | + } |
| 1524 | + /* we never get here, but appease static analysis */ |
| 1525 | + return; |
| 1526 | + } else { |
| 1527 | + /* parent */ |
| 1528 | + close(transcoder_stdin[0]); |
| 1529 | + out = fdopen(transcoder_stdin[1], "w"); |
| 1530 | + if (out == NULL) { |
| 1531 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1532 | + "fdopen failed: %s", |
| 1533 | + g_strerror(errno)); |
| 1534 | + return; |
| 1535 | + } |
| 1536 | + target->pid = pid; |
| 1537 | + target->writef = out; |
| 1538 | + /* resuming a transcode is not yet implemented */ |
| 1539 | + target->resume = FALSE; |
| 1540 | + } |
| 1541 | +} |
| 1542 | + |
| 1543 | +void |
| 1544 | +cleanup_transcode(LrTarget *target, GError **err) |
| 1545 | +{ |
| 1546 | + int wstatus, trc; |
| 1547 | + if (!target->writef) { |
| 1548 | + return; |
| 1549 | + } |
| 1550 | + if (target->writef == target->f) { |
| 1551 | + return; |
| 1552 | + } |
| 1553 | + fclose(target->writef); |
| 1554 | + if(waitpid(target->pid, &wstatus, 0) == -1) { |
| 1555 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1556 | + "transcode waitpid failed: %s", g_strerror(errno)); |
| 1557 | + } else if (WIFEXITED(wstatus)) { |
| 1558 | + trc = WEXITSTATUS(wstatus); |
| 1559 | + if (trc != 0) { |
| 1560 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1561 | + "transcode process non-zero exit code %d", trc); |
| 1562 | + } |
| 1563 | + } else if (WIFSIGNALED(wstatus)) { |
| 1564 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1565 | + "transcode process was terminated with a signal: %d", |
| 1566 | + WTERMSIG(wstatus)); |
| 1567 | + } else { |
| 1568 | + /* don't think this can happen, but covering all bases */ |
| 1569 | + g_set_error(err, LR_DOWNLOADER_ERROR, LRE_TRANSCODE, |
| 1570 | + "transcode unhandled circumstance in waitpid"); |
| 1571 | + } |
| 1572 | + target->writef = NULL; |
| 1573 | + /* pid is only valid if writef is not NULL */ |
| 1574 | + /* target->pid = -1; */ |
| 1575 | +} |
| 1576 | + |
1446 | 1577 | /** Prepare next transfer |
1447 | 1578 | */ |
1448 | 1579 | static gboolean |
@@ -1524,6 +1655,9 @@ prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err) |
1524 | 1655 | target->f = open_target_file(target, err); |
1525 | 1656 | if (!target->f) |
1526 | 1657 | goto fail; |
| 1658 | + maybe_transcode(target, err); |
| 1659 | + if (!target->writef) |
| 1660 | + goto fail; |
1527 | 1661 | target->writecb_recieved = 0; |
1528 | 1662 | target->writecb_required_range_written = FALSE; |
1529 | 1663 |
|
@@ -1699,6 +1833,7 @@ prepare_next_transfer(LrDownload *dd, gboolean *candidatefound, GError **err) |
1699 | 1833 | curl_easy_cleanup(target->curl_handle); |
1700 | 1834 | target->curl_handle = NULL; |
1701 | 1835 | } |
| 1836 | + cleanup_transcode(target, err); |
1702 | 1837 | if (target->f != NULL) { |
1703 | 1838 | fclose(target->f); |
1704 | 1839 | target->f = NULL; |
@@ -2269,6 +2404,8 @@ check_transfer_statuses(LrDownload *dd, GError **err) |
2269 | 2404 | if (transfer_err) // Transfer was unsuccessful |
2270 | 2405 | goto transfer_error; |
2271 | 2406 |
|
| 2407 | + cleanup_transcode(target, err); |
| 2408 | + |
2272 | 2409 | // |
2273 | 2410 | // Checksum checking |
2274 | 2411 | // |
@@ -2358,6 +2495,7 @@ check_transfer_statuses(LrDownload *dd, GError **err) |
2358 | 2495 | target->curl_handle = NULL; |
2359 | 2496 | g_free(target->headercb_interrupt_reason); |
2360 | 2497 | target->headercb_interrupt_reason = NULL; |
| 2498 | + cleanup_transcode(target, err); |
2361 | 2499 | fclose(target->f); |
2362 | 2500 | target->f = NULL; |
2363 | 2501 | if (target->curl_rqheaders) { |
@@ -2761,6 +2899,7 @@ lr_download(GSList *targets, |
2761 | 2899 | curl_multi_remove_handle(dd.multi_handle, target->curl_handle); |
2762 | 2900 | curl_easy_cleanup(target->curl_handle); |
2763 | 2901 | target->curl_handle = NULL; |
| 2902 | + cleanup_transcode(target, err); |
2764 | 2903 | fclose(target->f); |
2765 | 2904 | target->f = NULL; |
2766 | 2905 | g_free(target->headercb_interrupt_reason); |
|
0 commit comments