inotify: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: inotify
| Old | New | Line | |
|---|---|---|---|
| 1 | + | ## demo | |
| 2 | + | ||
| 3 | + | ### main program | |
| 4 | + | ||
| 5 | + | ``` pascal | |
| 6 | + | (** | |
| 7 | + | \brief Sample program showing inotify events. | |
| 8 | + | \version @0x5A4EEB4C | |
| 9 | + | \author Kai Burghardt <wiz at KaiBurghardt.de> | |
| 10 | + | \date 2018 | |
| 11 | + | \copyright All rights reserved. | |
| 12 | + | ||
| 13 | + | This program prints a readable representation of caught inotify events. | |
| 14 | + | It is non-interactive and does only watch a directory/file specified | |
| 15 | + | at compile-time. | |
| 16 | + | *) | |
| 17 | + | program inotifytst(input, output, stderr); | |
| 18 | + | ||
| 19 | + | {$mode objfpc} | |
| 20 | + | ||
| 21 | + | {$modeswitch classicprocvars on} | |
| 22 | + | ||
| 23 | + | {$typedAddress on} | |
| 24 | + | ||
| 25 | + | uses | |
| 26 | + | baseunix, linux, gettext, sysutils; | |
| 27 | + | ||
| 28 | + | const | |
| 29 | + | (** | |
| 30 | + | \brief directory/file to be monitored by inotify | |
| 31 | + | ||
| 32 | + | \note Not yet at run-time existing files are not treat specially. | |
| 33 | + | This program does not test whether watchedPath is a valid path. | |
| 34 | + | However, inotify does not add watches to non-existing paths. | |
| 35 | + | So inotify_add_watch may raise an error. | |
| 36 | + | *) | |
| 37 | + | watchedPath = '/tmp/abc'; | |
| 38 | + | /// \brief holds size of largest expected inotify_event size | |
| 39 | + | maxExpEventSize = sizeof(inotify_event) + name_max; | |
| 40 | + | ||
| 41 | + | // resource strings {} | |
| 42 | + | ||
| 43 | + | var | |
| 44 | + | /// \brief inotify file descriptor | |
| 45 | + | infd: cint; | |
| 46 | + | /// \brief inotify watch descriptor | |
| 47 | + | inwd: cint; | |
| 48 | + | /// \brief buffer for reading inotify events | |
| 49 | + | inEventQueueBuffer: PChar; | |
| 50 | + | bytesReadCount, bytesProcessedCount: TsSize; | |
| 51 | + | /// \brief a pointer to handle a single inotify_event | |
| 52 | + | inEvent: PInotify_event; | |
| 53 | + | /// \brief pointer to a signal action record | |
| 54 | + | newAction: PSigActionRec; | |
| 55 | + | ||
| 56 | + | (** | |
| 57 | + | \brief Closes inotify file descriptor. | |
| 58 | + | ||
| 59 | + | closeInFd closes the inotify file descriptor by invoking fpClose. | |
| 60 | + | It prints a warning on stderr if it went wrong but does not abort. | |
| 61 | + | *) | |
| 62 | + | procedure closeInFD; | |
| 63 | + | begin | |
| 64 | + | // when all fds referring to an inotify-instance have been closed, | |
| 65 | + | // the underlying object and its resources are automatically freed | |
| 66 | + | if fpClose(infd) <> 0 then | |
| 67 | + | begin | |
| 68 | + | writeln(stderr, warning + closeInotifyFDfailed); | |
| 69 | + | end; | |
| 70 | + | end; | |
| 71 | + | ||
| 72 | + | (** | |
| 73 | + | \brief Removes inotify watch | |
| 74 | + | ||
| 75 | + | cleanUpInWdNFd removes the only installed inotify watch | |
| 76 | + | from (the only) inotify watch descriptor. | |
| 77 | + | *) | |
| 78 | + | procedure cleanUpInWD; | |
| 79 | + | begin | |
| 80 | + | if inotify_rm_watch(infd, inwd) <> 0 then | |
| 81 | + | begin | |
| 82 | + | writeln(stderr, warning + disassocInwdFrInfdFailed); | |
| 83 | + | end; | |
| 84 | + | end; | |
| 85 | + | ||
| 86 | + | (** | |
| 87 | + | \brief frees reserved memory | |
| 88 | + | ||
| 89 | + | cleanUpEverything frees allocated memory which is reserved for | |
| 90 | + | operations in the main loop. | |
| 91 | + | ||
| 92 | + | \sa sigHandler | |
| 93 | + | *) | |
| 94 | + | procedure cleanUpMemory; | |
| 95 | + | begin | |
| 96 | + | freeMem(inEventQueueBuffer, maxExpEventSize); | |
| 97 | + | end; | |
| 98 | + | ||
| 99 | + | (** | |
| 100 | + | \brief procedure-wrapper for intended termination by signals | |
| 101 | + | ||
| 102 | + | sigHandler calls cleanUpEverything. | |
| 103 | + | It writes a final newline and halts program execution. | |
| 104 | + | *) | |
| 105 | + | procedure sigHandler( | |
| 106 | + | signal: longInt; | |
| 107 | + | info: PSigInfo; | |
| 108 | + | context: PSigContext | |
| 109 | + | ); noreturn; | |
| 110 | + | begin | |
| 111 | + | writeln; | |
| 112 | + | halt(0); | |
| 113 | + | end; | |
| 114 | + | ||
| 115 | + | (* *) | |
| 116 | + | (* M A I N *) | |
| 117 | + | (* *) | |
| 118 | + | ||
| 119 | + | begin | |
| 120 | + | //translateResourcestrings('/usr/share/locale/%s/LC_MESSAGES/' + | |
| 121 | + | // applicationName() + '.mo'); | |
| 122 | + | translateResourcestrings(applicationName() + '.%s.mo'); | |
| 123 | + | ||
| 124 | + | // function inotify_init() initializes a new inotify instance and | |
| 125 | + | // returns a file descriptor associated with its inotify event queue | |
| 126 | + | infd := inotify_init(); | |
| 127 | + | if infd < 0 then | |
| 128 | + | begin | |
| 129 | + | writeln(stderr, error + initializingInotifyFDfailed); | |
| 130 | + | // optional: further error analysis with fpgeterrno() | |
| 131 | + | halt(1); | |
| 132 | + | end; | |
| 133 | + | addExitProc(closeInFD); | |
| 134 | + | ||
| 135 | + | // next step: adding a watch to a previously initialized inotify instance | |
| 136 | + | inwd := inotify_add_watch(infd, watchedPath, in_all_events); | |
| 137 | + | if inwd < 0 then | |
| 138 | + | begin | |
| 139 | + | writeln(stderr, error + | |
| 140 | + | format(addingWatchedPathToInotifyWatchListFailed, [watchedPath])); | |
| 141 | + | // optional: retrieve errno with fpgeterrno() | |
| 142 | + | halt(2); | |
| 143 | + | end; | |
| 144 | + | addExitProc(cleanUpInWD); | |
| 145 | + | ||
| 146 | + | // avoid a run-time error on getMem-failure | |
| 147 | + | returnNilIfGrowHeapFails := true; | |
| 148 | + | // let's occupy some more more heap! | |
| 149 | + | inEventQueueBuffer := getMem(maxExpEventSize); | |
| 150 | + | // check on nil-pointer (failure) | |
| 151 | + | if not assigned(inEventQueueBuffer) then | |
| 152 | + | begin | |
| 153 | + | writeln(stderr, error + couldNotAllocateMemForInotifyEvents); | |
| 154 | + | halt(3); | |
| 155 | + | end; | |
| 156 | + | addExitProc(cleanUpMemory); | |
| 157 | + | ||
| 158 | + | // install a signal handler for several signals | |
| 159 | + | new(newAction); | |
| 160 | + | if not assigned(newAction) then | |
| 161 | + | begin | |
| 162 | + | writeln(stderr, error + preparingOfSignalHandlerInstallFailed); | |
| 163 | + | halt(4); | |
| 164 | + | end; | |
| 165 | + | ||
| 166 | + | // preparing signal action record | |
| 167 | + | // save address of sigHandler procedure | |
| 168 | + | newAction^.sa_handler := sigActionHandler(@sigHandler); | |
| 169 | + | fillchar(newAction^.sa_mask, sizeof(newAction^.sa_mask), #0); | |
| 170 | + | newAction^.sa_flags := 0; | |
| 171 | + | ||
| 172 | + | // pass sigActionRec to sigaction(2) syscall | |
| 173 | + | if fpSigAction(SigHup or SigInt or SigQuit or SigAbrt or SigTerm or | |
| 174 | + | SigTStp, newAction, nil) <> 0 then | |
| 175 | + | begin | |
| 176 | + | writeln(stderr, error + couldNotInstallSignalHandlerForSigint); | |
| 177 | + | halt(5); | |
| 178 | + | end; | |
| 179 | + | ||
| 180 | + | // get rid of unused newAction | |
| 181 | + | dispose(newAction); | |
| 182 | + | ||
| 183 | + | // TODO: find something better than | |
| 184 | + | // a while-true-loop and signal handling | |
| 185 | + | while true do | |
| 186 | + | begin | |
| 187 | + | // read() returns the amount of bytes have been read. | |
| 188 | + | // read() reads at most maxExpEventSize bytes to the memory position | |
| 189 | + | // specified by inEventQueueBuffer. | |
| 190 | + | // It's only possible to read whole inotify events, | |
| 191 | + | // but no fractions even though | |
| 192 | + | // the _beginning_ of the next event may fit into remaining space. | |
| 193 | + | bytesReadCount := fpRead(infd, inEventQueueBuffer, maxExpEventSize); | |
| 194 | + | if bytesReadCount < 0 then | |
| 195 | + | begin | |
| 196 | + | writeln(stderr, error + readingFromInotifyFDfailed); | |
| 197 | + | halt(6); | |
| 198 | + | end; | |
| 199 | + | ||
| 200 | + | // as there may be multiple inotify_events saved at inEventQueueBuffer | |
| 201 | + | // we have to keep a counter how much has been processed | |
| 202 | + | bytesProcessedCount := 0; | |
| 203 | + | ||
| 204 | + | // reset inEvent | |
| 205 | + | // typecast PChar to use it as a PInotify_event as such allows the | |
| 206 | + | // usage of dot-fieldname-designators | |
| 207 | + | inEvent := PInotify_event(inEventQueueBuffer); | |
| 208 | + | ||
| 209 | + | while bytesProcessedCount < bytesReadCount do | |
| 210 | + | begin | |
| 211 | + | // print a readable representation of current inotify_event | |
| 212 | + | writeln('inotify_event:'); | |
| 213 | + | with inEvent^ do | |
| 214 | + | begin | |
| 215 | + | writeln(' wd: ', wd); | |
| 216 | + | ||
| 217 | + | writeln(' mask: %', binStr(mask, 32)); | |
| 218 | + | // several tests on the mask {} | |
| 219 | + | ||
| 220 | + | writeln(' cookie: $', hexStr(cookie, 8)); | |
| 221 | + | ||
| 222 | + | // note: len does not contain the actual length of the name but | |
| 223 | + | // also counts any following null-characters needed for well | |
| 224 | + | // address-alignment | |
| 225 | + | writeln(' len: ', len); | |
| 226 | + | ||
| 227 | + | // The record's name field has been declared | |
| 228 | + | // to be a /single/ character. | |
| 229 | + | // Indeed we know it is a (dynamic) array of char | |
| 230 | + | // what corresponds to C's null-terminated strings. | |
| 231 | + | // Meanwhile writeln() is capable of | |
| 232 | + | // printing null-terminated strings, too. | |
| 233 | + | // All it needs is the information a given argument is such one. | |
| 234 | + | // Referring to inEvent^.name (single char) does not | |
| 235 | + | // but following typecasted pointer: | |
| 236 | + | writeln(' name: ', PChar(@inEvent^.name)); | |
| 237 | + | // end of with-inEvent^-do statement | |
| 238 | + | end; | |
| 239 | + | ||
| 240 | + | // inotify(7) says the size of an inotify_event is | |
| 241 | + | // sizeof(inotify_event) + len. but wait! it's a trap! | |
| 242 | + | // it's true for C, but in Pascal you got an off-by-one-error: | |
| 243 | + | // the inotify_event-record already | |
| 244 | + | // includes the first character of the name-field. | |
| 245 | + | inc(bytesProcessedCount, sizeof(inotify_event)-1 + inEvent^.len); | |
| 246 | + | inEvent := PInotify_event(@inEventQueueBuffer[sizeof(inotify_event)-1 + | |
| 247 | + | inEvent^.len]); | |
| 248 | + | // end of while-loop over every read inotify event | |
| 249 | + | end; | |
| 250 | + | // end of main-loop while true | |
| 251 | + | end; | |
| 252 | + | end. | |
| 253 | + | // vim: set tw=78 ts=4 noet bs=eol,start,indent fdm=marker fdc=1: | |
| 254 | + | ``` | |
| 255 | + | ||
| 256 | + | ### translations | |
| 257 | + | ||
| 258 | + | `inotifytst.de.po`: | |
| 259 | + | ||
| 260 | + | ```gettext | |
| 261 | + | #: inotifytst:warning | |
| 262 | + | msgid "warning: " | |
| 263 | + | msgstr "Warnung: " | |
| 264 | + | ||
| 265 | + | #: inotifytst:error | |
| 266 | + | msgid "error: " | |
| 267 | + | msgstr "Fehler: " | |
| 268 | + | ||
| 269 | + | #: inotifytst:closeinotifyfdfailed | |
| 270 | + | msgid "closing inotify file descriptor failed" | |
| 271 | + | msgstr "inotify Dateideskriptor schließen fehlgeschlagen" | |
| 272 | + | ||
| 273 | + | #: inotifytst:disassocinwdfrinfdfailed | |
| 274 | + | msgid "disassociating inwd from infd failed" | |
| 275 | + | msgstr "Trennung von inwd und infd fehlgeschlagen" | |
| 276 | + | ||
| 277 | + | #: inotifytst:initializinginotifyfdfailed | |
| 278 | + | msgid "initializing inotify file descriptor failed" | |
| 279 | + | msgstr "inotify Dateideskritpor initialisiieren fehlgeschlagen" | |
| 280 | + | ||
| 281 | + | #: inotifytst:addingwatchedpathtoinotifywatchlistfailed | |
| 282 | + | msgid "adding '%s' to inotify watch list failed" | |
| 283 | + | msgstr "hinzufügen von '%s' zur inotify Beobachtungsliste fehlgeschlagen" | |
| 284 | + | ||
| 285 | + | #: inotifytst:couldnotallocatememforinotifyevents | |
| 286 | + | msgid "could not allocate memory for inotify events" | |
| 287 | + | msgstr "konnte kein Speicher für inotify Ereignisse reservieren" | |
| 288 | + | ||
| 289 | + | #: inotifytst:preparingofsignalhandlerinstallfailed | |
| 290 | + | msgid "preparing of signal-handler install failed" | |
| 291 | + | msgstr "vorbereiten des Signalbehandlers Installation fehlgeschlagen" | |
| 292 | + | ||
| 293 | + | #: inotifytst:couldnotinstallsignalhandlerforsigint | |
| 294 | + | msgid "could not install signal handler for sigint" | |
| 295 | + | msgstr "konnte den Signalbehandler für das Unterbrechungssignal nicht installieren" | |
| 296 | + | ||
| 297 | + | #: inotifytst:readingfrominotifyfdfailed | |
| 298 | + | msgid "reading from inotify file descriptor failed" | |
| 299 | + | msgstr "vom inotify Dateideskriptor lesen fehlgeschlagen" | |
| 300 | + | ``` | |
| 301 | + | ||
| 302 | + | `inotifytst.en.po`: | |
| 303 | + | ||
| 304 | + | ```gettext | |
| 305 | + | #: inotifytst:warning | |
| 306 | + | msgid "warning: " | |
| 307 | + | msgstr "warning: " | |
| 308 | + | ||
| 309 | + | #: inotifytst:error | |
| 310 | + | msgid "error: " | |
| 311 | + | msgstr "error: " | |
| 312 | + | ||
| 313 | + | #: inotifytst:closeinotifyfdfailed | |
| 314 | + | msgid "closing inotify file descriptor failed" | |
| 315 | + | msgstr "closing inotify file descriptor failed" | |
| 316 | + | ||
| 317 | + | #: inotifytst:disassocinwdfrinfdfailed | |
| 318 | + | msgid "disassociating inwd from infd failed" | |
| 319 | + | msgstr "disassociating inwd from infd failed" | |
| 320 | + | ||
| 321 | + | #: inotifytst:initializinginotifyfdfailed | |
| 322 | + | msgid "initializing inotify file descriptor failed" | |
| 323 | + | msgstr "initializing inotify file descriptor failed" | |
| 324 | + | ||
| 325 | + | #: inotifytst:addingwatchedpathtoinotifywatchlistfailed | |
| 326 | + | msgid "adding '%s' to inotify watch list failed" | |
| 327 | + | msgstr "adding '%s' to inotify watch list failed" | |
| 328 | + | ||
| 329 | + | #: inotifytst:couldnotallocatememforinotifyevents | |
| 330 | + | msgid "could not allocate memory for inotify events" | |
| 331 | + | msgstr "could not allocate memory for inotify events" | |
| 332 | + | ||
| 333 | + | #: inotifytst:preparingofsignalhandlerinstallfailed | |
| 334 | + | msgid "preparing of signal-handler install failed" | |
| 335 | + | msgstr "preparing of signal-handler install failed" | |
| 336 | + | ||
| 337 | + | #: inotifytst:couldnotinstallsignalhandlerforsigint | |
| 338 | + | msgid "could not install signal handler for sigint" | |
| 339 | + | msgstr "could not install signal handler for sigint" | |
| 340 | + | ||
| 341 | + | #: inotifytst:readingfrominotifyfdfailed | |
| 342 | + | msgid "reading from inotify file descriptor failed" | |
| 343 | + | msgstr "reading from inotify file descriptor failed" | |
| 344 | + | ``` | |
| 345 | + | ||
| 346 | + | --- | |
| 347 | + | *From the Free Pascal wiki, page "inotify", as it stood on 2020-05-06 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 348 | + | Category: Code |
