1 /*	$NetBSD: dmeventd_snapshot.c,v 1.1.1.2 2009/12/02 00:27:12 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2007-2008 Red Hat, Inc. All rights reserved.
5  *
6  * This file is part of LVM2.
7  *
8  * This copyrighted material is made available to anyone wishing to use,
9  * modify, copy, or redistribute it subject to the terms and conditions
10  * of the GNU Lesser General Public License v.2.1.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
15  */
16 
17 #include "libdevmapper.h"
18 #include "libdevmapper-event.h"
19 #include "lvm2cmd.h"
20 #include "lvm-string.h"
21 
22 #include <errno.h>
23 #include <signal.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <pthread.h>
28 #include <unistd.h>
29 
30 #include <syslog.h> /* FIXME Replace syslog with multilog */
31 /* FIXME Missing openlog? */
32 
33 /* First warning when snapshot is 80% full. */
34 #define WARNING_THRESH 80
35 /* Further warnings at 85%, 90% and 95% fullness. */
36 #define WARNING_STEP 5
37 
38 static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER;
39 
40 /*
41  * Number of active registrations.
42  */
43 static int _register_count = 0;
44 
45 static struct dm_pool *_mem_pool = NULL;
46 static void *_lvm_handle = NULL;
47 
48 struct snap_status {
49 	int invalid;
50 	int used;
51 	int max;
52 };
53 
54 /*
55  * Currently only one event can be processed at a time.
56  */
57 static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER;
58 
59 static void _temporary_log_fn(int level,
60 			      const char *file __attribute((unused)),
61 			      int line __attribute((unused)),
62 			      int dm_errno __attribute((unused)),
63 			      const char *format)
64 {
65 	if (!strncmp(format, "WARNING: ", 9) && (level < 5))
66 		syslog(LOG_CRIT, "%s", format);
67 	else
68 		syslog(LOG_DEBUG, "%s", format);
69 }
70 
71 /* FIXME possibly reconcile this with target_percent when we gain
72    access to regular LVM library here. */
73 static void _parse_snapshot_params(char *params, struct snap_status *stat)
74 {
75 	char *p;
76 	/*
77 	 * xx/xx	-- fractions used/max
78 	 * Invalid	-- snapshot invalidated
79 	 * Unknown	-- status unknown
80 	 */
81 	stat->used = stat->max = 0;
82 
83 	if (!strncmp(params, "Invalid", 7)) {
84 		stat->invalid = 1;
85 		return;
86 	}
87 
88 	/*
89 	 * When we return without setting non-zero max, the parent is
90 	 * responsible for reporting errors.
91 	 */
92 	if (!strncmp(params, "Unknown", 7))
93 		return;
94 
95 	if (!(p = strstr(params, "/")))
96 		return;
97 
98 	*p = '\0';
99 	p++;
100 
101 	stat->used = atoi(params);
102 	stat->max = atoi(p);
103 }
104 
105 void process_event(struct dm_task *dmt,
106 		   enum dm_event_mask event __attribute((unused)),
107 		   void **private)
108 {
109 	void *next = NULL;
110 	uint64_t start, length;
111 	char *target_type = NULL;
112 	char *params;
113 	struct snap_status stat = { 0 };
114 	const char *device = dm_task_get_name(dmt);
115 	int percent, *percent_warning = (int*)private;
116 
117 	/* No longer monitoring, waiting for remove */
118 	if (!*percent_warning)
119 		return;
120 
121 	if (pthread_mutex_trylock(&_event_mutex)) {
122 		syslog(LOG_NOTICE, "Another thread is handling an event.  Waiting...");
123 		pthread_mutex_lock(&_event_mutex);
124 	}
125 
126 	dm_get_next_target(dmt, next, &start, &length, &target_type, &params);
127 	if (!target_type)
128 		goto out;
129 
130 	_parse_snapshot_params(params, &stat);
131 	/*
132 	 * If the snapshot has been invalidated or we failed to parse
133 	 * the status string. Report the full status string to syslog.
134 	 */
135 	if (stat.invalid || !stat.max) {
136 		syslog(LOG_ERR, "Snapshot %s changed state to: %s\n", device, params);
137 		*percent_warning = 0;
138 		goto out;
139 	}
140 
141 	percent = 100 * stat.used / stat.max;
142 	if (percent >= *percent_warning) {
143 		syslog(LOG_WARNING, "Snapshot %s is now %i%% full.\n", device, percent);
144 		/* Print warning on the next multiple of WARNING_STEP. */
145 		*percent_warning = (percent / WARNING_STEP) * WARNING_STEP + WARNING_STEP;
146 	}
147 out:
148 	pthread_mutex_unlock(&_event_mutex);
149 }
150 
151 int register_device(const char *device,
152 		    const char *uuid __attribute((unused)),
153 		    int major __attribute((unused)),
154 		    int minor __attribute((unused)),
155 		    void **private)
156 {
157 	int r = 0;
158 	int *percent_warning = (int*)private;
159 
160 	pthread_mutex_lock(&_register_mutex);
161 
162 	/*
163 	 * Need some space for allocations.  1024 should be more
164 	 * than enough for what we need (device mapper name splitting)
165 	 */
166 	if (!_mem_pool && !(_mem_pool = dm_pool_create("snapshot_dso", 1024)))
167 		goto out;
168 
169 	*percent_warning = WARNING_THRESH; /* Print warning if snapshot is full */
170 
171 	if (!_lvm_handle) {
172 		lvm2_log_fn(_temporary_log_fn);
173 		if (!(_lvm_handle = lvm2_init())) {
174 			dm_pool_destroy(_mem_pool);
175 			_mem_pool = NULL;
176 			goto out;
177 		}
178 		lvm2_log_level(_lvm_handle, LVM2_LOG_SUPPRESS);
179 		/* FIXME Temporary: move to dmeventd core */
180 		lvm2_run(_lvm_handle, "_memlock_inc");
181 	}
182 
183 	syslog(LOG_INFO, "Monitoring snapshot %s\n", device);
184 
185 	_register_count++;
186 	r = 1;
187 
188 out:
189 	pthread_mutex_unlock(&_register_mutex);
190 
191 	return r;
192 }
193 
194 int unregister_device(const char *device,
195 		      const char *uuid __attribute((unused)),
196 		      int major __attribute((unused)),
197 		      int minor __attribute((unused)),
198 		      void **unused __attribute((unused)))
199 {
200 	pthread_mutex_lock(&_register_mutex);
201 
202 	syslog(LOG_INFO, "No longer monitoring snapshot %s\n",
203 	       device);
204 
205 	if (!--_register_count) {
206 		dm_pool_destroy(_mem_pool);
207 		_mem_pool = NULL;
208 		lvm2_run(_lvm_handle, "_memlock_dec");
209 		lvm2_exit(_lvm_handle);
210 		_lvm_handle = NULL;
211 	}
212 
213 	pthread_mutex_unlock(&_register_mutex);
214 
215 	return 1;
216 }
217