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