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 for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) 42 root->ReleaseReference(); 43 fRoots.MakeEmpty(false); 44 } 45 46 47 status_t 48 PackageDaemon::Init() 49 { 50 status_t error = fVolumeWatcher.StartWatching(BMessenger(this, this)); 51 if (error != B_OK) { 52 ERROR("PackageDaemon::Init(): failed to start volume watching: %s\n", 53 strerror(error)); 54 } 55 56 // register all packagefs volumes 57 for (int32 cookie = 0;;) { 58 dev_t device = next_dev(&cookie); 59 if (device < 0) 60 break; 61 62 _RegisterVolume(device); 63 } 64 65 return B_OK; 66 } 67 68 69 void 70 PackageDaemon::MessageReceived(BMessage* message) 71 { 72 switch (message->what) { 73 case B_NODE_MONITOR: 74 { 75 int32 opcode; 76 if (message->FindInt32("opcode", &opcode) != B_OK) 77 break; 78 79 if (opcode == B_DEVICE_MOUNTED) 80 _HandleVolumeMounted(message); 81 else if (opcode == B_DEVICE_UNMOUNTED) 82 _HandleVolumeUnmounted(message); 83 break; 84 } 85 86 case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO: 87 case B_MESSAGE_COMMIT_TRANSACTION: 88 { 89 status_t error; 90 node_ref nodeRef; 91 92 // Get the node_ref of the filesystem root to see which one it is 93 error = message->FindInt32("volume", &nodeRef.device); 94 if (error == B_OK) 95 error = message->FindInt64("root", &nodeRef.node); 96 97 if (fSystemRoot != NULL && (error != B_OK 98 || fSystemRoot->NodeRef() == nodeRef)) { 99 fSystemRoot->HandleRequest(DetachCurrentMessage()); 100 } else if (error == B_OK) { 101 Root* root = _FindRoot(nodeRef); 102 if (root != NULL) { 103 root->HandleRequest(DetachCurrentMessage()); 104 } 105 } 106 break; 107 } 108 109 default: 110 BServer::MessageReceived(message); 111 break; 112 } 113 } 114 115 116 status_t 117 PackageDaemon::_RegisterVolume(dev_t deviceID) 118 { 119 // get the FS info and check whether this is a package FS volume at all 120 fs_info info; 121 status_t error = fs_stat_dev(deviceID, &info); 122 if (error != 0) 123 RETURN_ERROR(error); 124 125 if (strcmp(info.fsh_name, "packagefs") != 0) 126 RETURN_ERROR(B_BAD_VALUE); 127 128 // create a volume 129 Volume* volume = new(std::nothrow) Volume(this); 130 if (volume == NULL) 131 RETURN_ERROR(B_NO_MEMORY); 132 ObjectDeleter<Volume> volumeDeleter(volume); 133 134 node_ref rootRef; 135 error = volume->Init(node_ref(info.dev, info.root), rootRef); 136 if (error != B_OK) 137 RETURN_ERROR(error); 138 139 if (volume->MountType() == PACKAGE_FS_MOUNT_TYPE_CUSTOM) { 140 // TODO: Or maybe not? 141 INFORM("skipping custom mounted volume at \"%s\"\n", 142 volume->Path().String()); 143 return B_OK; 144 } 145 146 // get the root for the volume and register it 147 Root* root; 148 error = _GetOrCreateRoot(rootRef, root); 149 if (error != B_OK) 150 RETURN_ERROR(error); 151 152 error = root->RegisterVolume(volume); 153 if (error != B_OK) { 154 _PutRoot(root); 155 RETURN_ERROR(error); 156 } 157 volumeDeleter.Detach(); 158 159 INFORM("volume at \"%s\" registered\n", volume->Path().String()); 160 161 return B_OK; 162 } 163 164 165 void 166 PackageDaemon::_UnregisterVolume(Volume* volume) 167 { 168 volume->Unmounted(); 169 170 INFORM("volume at \"%s\" unregistered\n", volume->Path().String()); 171 172 Root* root = volume->GetRoot(); 173 root->UnregisterVolume(volume); 174 175 _PutRoot(root); 176 } 177 178 179 status_t 180 PackageDaemon::_GetOrCreateRoot(const node_ref& nodeRef, Root*& _root) 181 { 182 Root* root = _FindRoot(nodeRef); 183 if (root != NULL) { 184 root->AcquireReference(); 185 } else { 186 root = new(std::nothrow) Root; 187 if (root == NULL) 188 RETURN_ERROR(B_NO_MEMORY); 189 ObjectDeleter<Root> rootDeleter(root); 190 191 bool isSystemRoot = false; 192 if (fSystemRoot == NULL) { 193 struct stat st; 194 isSystemRoot = stat("/boot", &st) == 0 195 && node_ref(st.st_dev, st.st_ino) == nodeRef; 196 } 197 198 status_t error = root->Init(nodeRef, isSystemRoot); 199 if (error != B_OK) 200 RETURN_ERROR(error); 201 202 if (!fRoots.AddItem(root)) 203 RETURN_ERROR(B_NO_MEMORY); 204 205 rootDeleter.Detach(); 206 207 if (isSystemRoot) 208 fSystemRoot = root; 209 210 INFORM("root at \"%s\" (device: %" B_PRIdDEV ", node: %" B_PRIdINO ") " 211 "registered\n", root->Path().String(), nodeRef.device, 212 nodeRef.node); 213 } 214 215 _root = root; 216 return B_OK; 217 } 218 219 220 Root* 221 PackageDaemon::_FindRoot(const node_ref& nodeRef) const 222 { 223 for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) { 224 if (root->NodeRef() == nodeRef) 225 return root; 226 } 227 228 return NULL; 229 } 230 231 232 void 233 PackageDaemon::_PutRoot(Root* root) 234 { 235 if (root->ReleaseReference() == 1) { 236 INFORM("root at \"%s\" unregistered\n", root->Path().String()); 237 fRoots.RemoveItem(root, true); 238 // deletes the object 239 } 240 } 241 242 243 Volume* 244 PackageDaemon::_FindVolume(dev_t deviceID) const 245 { 246 for (int32 i = 0; Root* root = fRoots.ItemAt(i); i++) { 247 if (Volume* volume = root->FindVolume(deviceID)) 248 return volume; 249 } 250 251 return NULL; 252 } 253 254 255 void 256 PackageDaemon::_HandleVolumeMounted(const BMessage* message) 257 { 258 int32 device; 259 if (message->FindInt32("new device", &device) != B_OK) 260 return; 261 262 // _RegisterVolume() also checks whether it is a package FS volume, so we 263 // don't need to bother. 264 _RegisterVolume(device); 265 } 266 267 268 void 269 PackageDaemon::_HandleVolumeUnmounted(const BMessage* message) 270 { 271 int32 device; 272 if (message->FindInt32("device", &device) != B_OK) 273 return; 274 275 if (Volume* volume = _FindVolume(device)) 276 _UnregisterVolume(volume); 277 } 278 279 280 // #pragma mark - 281 282 283 int 284 main(int argc, const char* const* argv) 285 { 286 status_t error; 287 PackageDaemon daemon(&error); 288 if (error == B_OK) 289 error = daemon.Init(); 290 if (error != B_OK) { 291 FATAL("failed to init server application: %s\n", strerror(error)); 292 return 1; 293 } 294 295 daemon.Run(); 296 return 0; 297 } 298