1 /* 2 * Copyright 2013, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold <ingo_weinhold@gmx.de> 7 */ 8 9 10 #include "PackageDaemon.h" 11 12 #include <errno.h> 13 #include <string.h> 14 15 #include <Directory.h> 16 #include <NodeMonitor.h> 17 18 #include <AutoDeleter.h> 19 #include <package/DaemonDefs.h> 20 21 #include "DebugSupport.h" 22 #include "Root.h" 23 #include "Volume.h" 24 25 26 using namespace BPackageKit::BPrivate; 27 28 29 PackageDaemon::PackageDaemon(status_t* _error) 30 : 31 BServer(B_PACKAGE_DAEMON_APP_SIGNATURE, false, _error), 32 fSystemRoot(NULL), 33 fRoots(10, true), 34 fVolumeWatcher() 35 { 36 } 37 38 39 PackageDaemon::~PackageDaemon() 40 { 41 } 42 43 44 status_t 45 PackageDaemon::Init() 46 { 47 status_t error = fVolumeWatcher.StartWatching(BMessenger(this, this)); 48 if (error != B_OK) { 49 ERROR("PackageDaemon::Init(): failed to start volume watching: %s\n", 50 strerror(error)); 51 } 52 53 // register all packagefs volumes 54 for (int32 cookie = 0;;) { 55 dev_t device = next_dev(&cookie); 56 if (device < 0) 57 break; 58 59 _RegisterVolume(device); 60 } 61 62 // find the system root 63 struct stat st; 64 if (stat("/boot", &st) == 0) 65 fSystemRoot = _FindRoot(node_ref(st.st_dev, st.st_ino)); 66 67 return B_OK; 68 } 69 70 71 void 72 PackageDaemon::MessageReceived(BMessage* message) 73 { 74 switch (message->what) { 75 case B_NODE_MONITOR: 76 { 77 int32 opcode; 78 if (message->FindInt32("opcode", &opcode) != B_OK) 79 break; 80 81 if (opcode == B_DEVICE_MOUNTED) 82 _HandleVolumeMounted(message); 83 else if (opcode == B_DEVICE_UNMOUNTED) 84 _HandleVolumeUnmounted(message); 85 break; 86 } 87 88 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO: 89 case B_MESSAGE_COMMIT_TRANSACTION: 90 { 91 if (fSystemRoot != NULL) 92 fSystemRoot->HandleRequest(DetachCurrentMessage()); 93 break; 94 } 95 96 default: 97 BServer::MessageReceived(message); 98 break; 99 } 100 } 101 102 103 status_t 104 PackageDaemon::_RegisterVolume(dev_t deviceID) 105 { 106 // get the FS info and check whether this is a package FS volume at all 107 fs_info info; 108 status_t error = fs_stat_dev(deviceID, &info); 109 if (error != 0) 110 RETURN_ERROR(error); 111 112 if (strcmp(info.fsh_name, "packagefs") != 0) 113 RETURN_ERROR(B_BAD_VALUE); 114 115 // create a volume 116 Volume* volume = new(std::nothrow) Volume(this); 117 if (volume == NULL) 118 RETURN_ERROR(B_NO_MEMORY); 119 ObjectDeleter<Volume> volumeDeleter(volume); 120 121 node_ref rootRef; 122 error = volume->Init(node_ref(info.dev, info.root), rootRef); 123 if (error != B_OK) 124 RETURN_ERROR(error); 125 126 if (volume->MountType() == PACKAGE_FS_MOUNT_TYPE_CUSTOM) { 127 // TODO: Or maybe not? 128 INFORM("skipping custom mounted volume at \"%s\"\n", 129 volume->Path().String()); 130 return B_OK; 131 } 132 133 // get the root for the volume and register it 134 Root* root; 135 error = _GetOrCreateRoot(rootRef, root); 136 if (error != B_OK) 137 RETURN_ERROR(error); 138 139 error = root->RegisterVolume(volume); 140 if (error != B_OK) { 141 _PutRoot(root); 142 RETURN_ERROR(error); 143 } 144 volumeDeleter.Detach(); 145 146 INFORM("volume at \"%s\" registered\n", volume->Path().String()); 147 148 return B_OK; 149 } 150 151 152 void 153 PackageDaemon::_UnregisterVolume(Volume* volume) 154 { 155 volume->Unmounted(); 156 157 INFORM("volume at \"%s\" unregistered\n", volume->Path().String()); 158 159 Root* root = volume->GetRoot(); 160 root->UnregisterVolume(volume); 161 162 _PutRoot(root); 163 } 164 165 166 status_t 167 PackageDaemon::_GetOrCreateRoot(const node_ref& nodeRef, Root*& _root) 168 { 169 Root* root = _FindRoot(nodeRef); 170 if (root != NULL) { 171 root->AcquireReference(); 172 } else { 173 root = new(std::nothrow) Root; 174 if (root == NULL) 175 RETURN_ERROR(B_NO_MEMORY); 176 ObjectDeleter<Root> rootDeleter(root); 177 178 status_t error = root->Init(nodeRef); 179 if (error != B_OK) 180 RETURN_ERROR(error); 181 182 if (!fRoots.AddItem(root)) 183 RETURN_ERROR(B_NO_MEMORY); 184 185 rootDeleter.Detach(); 186 187 INFORM("root at \"%s\" (device: %" B_PRIdDEV ", node: %" B_PRIdINO ") " 188 "registered\n", root->Path().String(), nodeRef.device, 189 nodeRef.node); 190 } 191 192 _root = root; 193 return B_OK; 194 } 195 196 197 Root* 198 PackageDaemon::_FindRoot(const node_ref& nodeRef) const 199 { 200 for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) { 201 if (root->NodeRef() == nodeRef) 202 return root; 203 } 204 205 return NULL; 206 } 207 208 209 void 210 PackageDaemon::_PutRoot(Root* root) 211 { 212 if (root->ReleaseReference() == 1) { 213 INFORM("root at \"%s\" unregistered\n", root->Path().String()); 214 fRoots.RemoveItem(root, true); 215 // deletes the object 216 } 217 } 218 219 220 Volume* 221 PackageDaemon::_FindVolume(dev_t deviceID) const 222 { 223 for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) { 224 if (Volume* volume = root->FindVolume(deviceID)) 225 return volume; 226 } 227 228 return NULL; 229 } 230 231 232 void 233 PackageDaemon::_HandleVolumeMounted(const BMessage* message) 234 { 235 int32 device; 236 if (message->FindInt32("new device", &device) != B_OK) 237 return; 238 239 // _RegisterVolume() also checks whether it is a package FS volume, so we 240 // don't need to bother. 241 _RegisterVolume(device); 242 } 243 244 245 void 246 PackageDaemon::_HandleVolumeUnmounted(const BMessage* message) 247 { 248 int32 device; 249 if (message->FindInt32("device", &device) != B_OK) 250 return; 251 252 if (Volume* volume = _FindVolume(device)) 253 _UnregisterVolume(volume); 254 } 255 256 257 // #pragma mark - 258 259 260 int 261 main(int argc, const char* const* argv) 262 { 263 status_t error; 264 PackageDaemon daemon(&error); 265 if (error == B_OK) 266 error = daemon.Init(); 267 if (error != B_OK) { 268 FATAL("failed to init server application: %s\n", strerror(error)); 269 return 1; 270 } 271 272 daemon.Run(); 273 return 0; 274 } 275