/* * iDescriptor: A free and open-source idevice management tool. * * Copyright (C) 2025 Uncore * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #ifndef SERVICEMANAGER_H #define SERVICEMANAGER_H #include "iDescriptor.h" #include #include #include #include #include /** * @brief Centralized manager for device service operations with thread safety * * This class provides thread-safe wrappers for all device operations to prevent * crashes when devices are unplugged during active operations. It uses a * per-device recursive mutex to ensure that device cleanup waits for all * operations to complete. */ class ServiceManager { public: template static T executeOperation(iDescriptorDevice *device, std::function operation, std::optional altAfc = std::nullopt) { if (!device || !device->mutex) { return T{}; // Return default-constructed value for the type } std::lock_guard lock(*device->mutex); // Double-check device is still valid after acquiring lock if (!device->afcClient) { return T{}; } if (altAfc && !*altAfc) { // altAfc was explicitly provided but is null, which is an // invalid state. return T{}; } // Determine which client to use afc_client_t client = altAfc ? *altAfc : device->afcClient; return operation(client); } template static T executeOperation(iDescriptorDevice *device, std::function operation, std::optional altAfc = std::nullopt) { if (!device || !device->mutex) { return T{}; // Return default-constructed value for the type } std::lock_guard lock(*device->mutex); // Double-check device is still valid after acquiring lock if (!device->afcClient) { return T{}; } if (altAfc && !*altAfc) { // altAfc was explicitly provided but is null, which is an // invalid state. return T{}; } return operation(); } template static T executeOperation(iDescriptorDevice *device, std::function operation, T failureValue, std::optional altAfc = std::nullopt) { if (!device || !device->mutex) { return failureValue; } std::lock_guard lock(*device->mutex); // Double-check device is still valid after acquiring lock if (!device->afcClient) { return failureValue; } if (altAfc && !*altAfc) { // altAfc was explicitly provided but is null, which is an // invalid state. return failureValue; } return operation(); } static void executeOperation(iDescriptorDevice *device, std::function operation, std::optional altAfc = std::nullopt) { if (!device || !device->mutex) { return; } std::lock_guard lock(*device->mutex); // Double-check device is still valid after acquiring lock if (!device->afcClient) { return; } if (altAfc && !*altAfc) { // altAfc was explicitly provided but is null, which is an // invalid state. return; } operation(); } static afc_error_t executeAfcOperation(iDescriptorDevice *device, std::function operation, std::optional altAfc = std::nullopt) { try { if (!device || !device->mutex) { return AFC_E_UNKNOWN_ERROR; } std::lock_guard lock(*device->mutex); // Double-check device is still valid after acquiring lock if (!device->afcClient) { return AFC_E_UNKNOWN_ERROR; } if (altAfc && !*altAfc) { // altAfc was explicitly provided but is null, which is an // invalid state. return AFC_E_INVALID_ARG; } // Determine which client to use afc_client_t client = altAfc ? *altAfc : device->afcClient; return operation(client); } catch (const std::exception &e) { qDebug() << "Exception in executeAfcOperation:" << e.what(); return AFC_E_UNKNOWN_ERROR; } } // Specific AFC operation wrappers static afc_error_t safeAfcReadDirectory(iDescriptorDevice *device, const char *path, char ***dirs, std::optional altAfc = std::nullopt); static afc_error_t safeAfcGetFileInfo(iDescriptorDevice *device, const char *path, char ***info, std::optional altAfc = std::nullopt); static afc_error_t safeAfcGetFileInfoPlist(iDescriptorDevice *device, const char *path, plist_t *info, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileOpen(iDescriptorDevice *device, const char *path, afc_file_mode_t mode, uint64_t *handle, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileRead(iDescriptorDevice *device, uint64_t handle, char *data, uint32_t length, uint32_t *bytes_read, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileWrite(iDescriptorDevice *device, uint64_t handle, const char *data, uint32_t length, uint32_t *bytes_written, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileClose(iDescriptorDevice *device, uint64_t handle, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileSeek(iDescriptorDevice *device, uint64_t handle, int64_t offset, int whence, std::optional altAfc = std::nullopt); static afc_error_t safeAfcFileTell(iDescriptorDevice *device, uint64_t handle, uint64_t *position, std::optional altAfc = std::nullopt); // Utility functions static QByteArray safeReadAfcFileToByteArray( iDescriptorDevice *device, const char *path, std::optional altAfc = std::nullopt); static AFCFileTree safeGetFileTree(iDescriptorDevice *device, const std::string &path = "/", bool checkDir = true, std::optional altAfc = std::nullopt); }; #endif // SERVICEMANAGER_H