FFmpegfs Fuse Multi Media Filesystem 2.16
cache_maintenance.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 2017-2024 Norbert Schlia (nschlia@oblivion-software.de)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 * On Debian systems, the complete text of the GNU General Public License
19 * Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
20 */
21
32#include "cache_maintenance.h"
33#include "ffmpegfs.h"
34#include "logging.h"
35
36#include <csignal>
37#include <unistd.h>
38#include <sys/shm.h> /* shmat(), IPC_RMID */
39#include <semaphore.h> /* sem_open(), sem_destroy(), sem_wait().. */
40
41#define CLOCKID CLOCK_REALTIME
42#define SIGMAINT SIGRTMIN
44#define SEM_OPEN_FILE "/" PACKAGE_NAME "_04806785-b5fb-4615-ba56-b30a2946e80b"
46static sigset_t mask;
47static timer_t timerid;
49static sem_t * sem;
50static int shmid;
51static pid_t * pid_master;
52static bool master;
54static void maintenance_handler(int sig, __attribute__((unused)) siginfo_t *si, __attribute__((unused)) void *uc);
55static bool start_timer(time_t interval);
56static bool stop_timer();
57static bool link_up();
58static void master_check();
59static bool link_down();
60
67static void maintenance_handler(int sig, __attribute__((unused)) siginfo_t *si, __attribute__((unused)) void *uc)
68{
69 if (sig != SIGMAINT)
70 {
71 // Wrong signal. Should never happen.
72 return;
73 }
74
76
77 if (master)
78 {
79 Logging::info(nullptr, "Running periodic cache maintenance.");
81 }
82}
83
89static bool start_timer(time_t interval)
90{
91 struct sigevent sev;
92 struct itimerspec its;
93 long long freq_nanosecs;
94 struct sigaction sa;
95
96 freq_nanosecs = interval * 1000000000LL;
97
98 Logging::trace(nullptr, "Starting maintenance timer with %1period.", format_time(interval).c_str());
99
100 // Establish maintenance_handler for timer signal
101 sa.sa_flags = SA_SIGINFO;
102 sa.sa_sigaction = maintenance_handler;
103 sigemptyset(&sa.sa_mask);
104 if (sigaction(SIGMAINT, &sa, nullptr) == -1)
105 {
106 Logging::error(nullptr, "start_timer(): sigaction failed: (%1) %2", errno, strerror(errno));
107 return false;
108 }
109
110 // Block timer signal temporarily
111 sigemptyset(&mask);
112 sigaddset(&mask, SIGMAINT);
113 if (sigprocmask(SIG_SETMASK, &mask, nullptr) == -1)
114 {
115 Logging::error(nullptr, "start_timer(): sigprocmask(SIG_SETMASK) failed: (%1) %2", errno, strerror(errno));
116 return false;
117 }
118
119 // Create the timer
120 sev.sigev_notify = SIGEV_SIGNAL;
121 sev.sigev_signo = SIGMAINT;
122 sev.sigev_value.sival_ptr = &timerid;
123 if (timer_create(CLOCKID, &sev, &timerid) == -1)
124 {
125 Logging::error(nullptr, "start_timer(): timer_create failed: (%1) %2", errno, strerror(errno));
126 return false;
127 }
128
129 // Start the timer
130 its.it_value.tv_sec = static_cast<time_t>(freq_nanosecs / 1000000000);
131 its.it_value.tv_nsec = static_cast<long>(freq_nanosecs % 1000000000);
132 its.it_interval.tv_sec = its.it_value.tv_sec;
133 its.it_interval.tv_nsec = its.it_value.tv_nsec;
134
135 if (timer_settime(timerid, 0, &its, nullptr) == -1)
136 {
137 Logging::error(nullptr, "start_timer(): timer_settime failed: (%1) %2", errno, strerror(errno));
138 return false;
139 }
140
141 if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1)
142 {
143 Logging::error(nullptr, "start_timer(): sigprocmask(SIG_UNBLOCK) failed: (%1) %2", errno, strerror(errno));
144 }
145
146 Logging::trace(nullptr, "The maintenance timer started successfully.");
147
148 return true;
149}
150
155static bool stop_timer()
156{
157 Logging::info(nullptr, "Stopping the maintenance timer.");
158
159 if (timer_delete(timerid) == -1 && errno)
160 {
161 Logging::error(nullptr, "stop_timer(): timer_delete failed: (%1) %2", errno, strerror(errno));
162 return false;
163 }
164
165 return true;
166}
167
172static bool link_up()
173{
174 key_t shmkey;
175
176 Logging::debug(nullptr, "Activating " PACKAGE " inter-process link.");
177
178 // initialise a shared variable in shared memory
179 shmkey = ftok ("/dev/null", 5); // valid directory name and a number
180
181 if (shmkey == -1)
182 {
183 Logging::error(nullptr, "link_up(): ftok error (%1) %2", errno, strerror(errno));
184 return false;
185 }
186
187 // First try to open existing memory.
188 shmid = shmget (shmkey, sizeof (pid_t), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
189 if (shmid != -1)
190 {
191 // Shared memory already exists, seems we are client.
192 master = false;
193 }
194 else
195 {
196 // Ignore error at first, try to create memory.
197 shmid = shmget (shmkey, sizeof (pid_t), IPC_CREAT | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
198 if (shmid != -1)
199 {
200 // Shared memory freshly created, seems we are master.
201 master = true;
202 }
203 else
204 {
205 Logging::error(nullptr, "link_up(): shmget error (%1) %2", errno, strerror(errno));
206 return false;
207 }
208 }
209
210 pid_master = static_cast<pid_t *>(shmat (shmid, nullptr, 0)); // attach pid_master to shared memory
211
212 if (master)
213 {
214 *pid_master = getpid();
215 Logging::info(nullptr, "The process with PID %1 is now master.", *pid_master);
216 }
217 else
218 {
219 Logging::info(nullptr, "The process with PID %1 is now a client, and PID %2 is the master.", getpid(), *pid_master);
220 }
221
222 // Also create inter-process semaphore.
223 sem = sem_open(const_cast<const char *>(SEM_OPEN_FILE), O_CREAT, 0777, 1);
224
225 if (sem == SEM_FAILED)
226 {
227 Logging::error(nullptr, "link_up(): sem_open error (%1) %2", errno, strerror(errno));
228 link_down();
229 return false;
230 }
231
232 return true;
233}
234
238static void master_check()
239{
240 pid_t pid_self = getpid();
241
242 if (*pid_master == pid_self)
243 {
244 Logging::trace(nullptr, "The process with PID %1 is already master.", pid_self);
245 return;
246 }
247
248 sem_wait(sem);
249
250 // Check if master process still exists
251 int master_running = (getpgid(*pid_master) >= 0);
252
253 Logging::trace(nullptr, "Master with PID %1 is %2 running.", *pid_master, master_running ? "still" : "NOT");
254
255 if (!master_running)
256 {
257 Logging::info(nullptr, "Master with PID %1 is gone. PID %2 taking over as new master.", *pid_master, pid_self);
258
259 // Register us as master
260 *pid_master = pid_self;
261 master = true;
262 }
263
264 sem_post(sem);
265}
266
271static bool link_down()
272{
273 struct shmid_ds buf;
274 bool success = true;
275
276 Logging::info(nullptr, "Shutting " PACKAGE " inter-process link down.");
277
278 if (sem != nullptr && sem_close(sem))
279 {
280 Logging::error(nullptr, "link_down(): sem_close error (%1) %2", errno, strerror(errno));
281 success = false;
282 }
283
284 // shared memory detach
285 if (shmdt (pid_master))
286 {
287 Logging::error(nullptr, "link_down(): shmdt error (%1) %2", errno, strerror(errno));
288 success = false;
289 }
290
291 if (shmctl(shmid, IPC_STAT, &buf))
292 {
293 Logging::error(nullptr, "link_down(): shmctl error (%1) %2", errno, strerror(errno));
294 success = false;
295 }
296 else
297 {
298 if (!buf.shm_nattch)
299 {
300 if (shmctl (shmid, IPC_RMID, nullptr))
301 {
302 Logging::error(nullptr, "link_down(): shmctl error (%1) %2", errno, strerror(errno));
303 success = false;
304 }
305
306 // unlink prevents the semaphore existing forever
307 // if a crash occurs during the execution
308 if (sem_unlink(SEM_OPEN_FILE))
309 {
310 Logging::error(nullptr, "link_down(): sem_unlink error (%1) %2", errno, strerror(errno));
311 success = false;
312 }
313 }
314 }
315
316 return success;
317}
318
319bool start_cache_maintenance(time_t interval)
320{
321 // Start link
322 if (!link_up())
323 {
324 return false;
325 }
326
327 // Now start timer
328 return start_timer(interval);
329}
330
332{
333 bool success = true;
334
335 // Stop timer first
336 if (!stop_timer())
337 {
338 success = false;
339 }
340
341 // Now shut down link
342 if (!link_down())
343 {
344 success = false;
345 }
346
347 return success;
348}
static bool master
If true, we are master.
bool stop_cache_maintenance()
Stop cache maintenance timer.
#define CLOCKID
Use real time clock here.
static void maintenance_handler(int sig, __attribute__((unused)) siginfo_t *si, __attribute__((unused)) void *uc)
Run maintenance handler.
static sigset_t mask
Process mask for timer.
static bool start_timer(time_t interval)
Start the maintenance timer at predefined interval.
static sem_t * sem
Semaphore used to synchronise between master and slave processes.
#define SIGMAINT
Map maintenance signal.
#define SEM_OPEN_FILE
Shared semaphore name, should be unique system wide.
static bool link_down()
Set system wide inter process link down.
static timer_t timerid
Timer id.
static bool link_up()
Set system wide inter process link up.
static int shmid
Shared memory segment ID.
static bool stop_timer()
Stop the maintenance timer.
bool start_cache_maintenance(time_t interval)
Start cache maintenance timer.
static pid_t * pid_master
PID of master process.
static void master_check()
Check if a master is already running. We become master if not.
Cache maintenance
static void debug(const T filename, const std::string &format_string, Args &&...args)
Write debug level log entry.
Definition: logging.h:182
static void trace(const T filename, const std::string &format_string, Args &&...args)
Write trace level log entry.
Definition: logging.h:163
static void info(const T filename, const std::string &format_string, Args &&...args)
Write info level log entry.
Definition: logging.h:201
static void error(const T filename, const std::string &format_string, Args &&...args)
Write error level log entry.
Definition: logging.h:239
std::string format_time(time_t value)
Format a time in format "w d m s".
Main include for FFmpegfs project.
bool transcoder_cache_maintenance()
Run cache maintenance.
Definition: transcode.cc:743
Provide various log facilities to stderr, disk or syslog.