00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <config.h>
00021
00022 #include "sbuild-lock.h"
00023
00024 #include <cerrno>
00025 #include <cstdlib>
00026
00027 #include <unistd.h>
00028
00029 #include <boost/format.hpp>
00030
00031 #include <lockdev.h>
00032
00033 using boost::format;
00034 using namespace sbuild;
00035
00036 namespace
00037 {
00038
00039 typedef std::pair<lock::error_code,const char *> emap;
00040
00045 emap init_errors[] =
00046 {
00047 emap(lock::TIMEOUT_HANDLER, N_("Failed to set timeout handler")),
00048 emap(lock::TIMEOUT_SET, N_("Failed to set timeout")),
00049 emap(lock::TIMEOUT_CANCEL, N_("Failed to cancel timeout")),
00050 emap(lock::LOCK, N_("Failed to lock file")),
00051
00052 emap(lock::LOCK_TIMEOUT, N_("Failed to lock file (timed out after %4% seconds)")),
00053 emap(lock::DEVICE_LOCK, N_("Failed to lock device")),
00054
00055
00056 emap(lock::DEVICE_LOCK_TIMEOUT, N_("Failed to lock device (timed out after %4% seconds; lock held by PID %5%)")),
00057 emap(lock::DEVICE_TEST, N_("Failed to test device lock")),
00058 emap(lock::DEVICE_UNLOCK, N_("Failed to unlock device")),
00059
00060
00061 emap(lock::DEVICE_UNLOCK, N_("Failed to unlock device (timed out after %4% seconds; lock held by PID %5%)"))
00062 };
00063
00064 }
00065
00066 template<>
00067 error<lock::error_code>::map_type
00068 error<lock::error_code>::error_strings
00069 (init_errors,
00070 init_errors + (sizeof(init_errors) / sizeof(init_errors[0])));
00071
00072 namespace
00073 {
00074
00075 volatile bool lock_timeout = false;
00076
00082 void
00083 alarm_handler (int ignore)
00084 {
00085
00086
00087
00088 lock_timeout = true;
00089 }
00090 }
00091
00092 lock::lock ():
00093 saved_signals()
00094 {
00095 }
00096
00097 lock::~lock ()
00098 {
00099 }
00100
00101 void
00102 lock::set_alarm ()
00103 {
00104 struct sigaction new_sa;
00105 sigemptyset(&new_sa.sa_mask);
00106 new_sa.sa_flags = 0;
00107 new_sa.sa_handler = alarm_handler;
00108
00109 if (sigaction(SIGALRM, &new_sa, &this->saved_signals) != 0)
00110 throw error(TIMEOUT_HANDLER, strerror(errno));
00111 }
00112
00113 void
00114 lock::clear_alarm ()
00115 {
00116
00117 sigaction (SIGALRM, &this->saved_signals, 0);
00118 }
00119
00120 void
00121 lock::set_timer(struct itimerval const& timer)
00122 {
00123 set_alarm();
00124
00125 if (setitimer(ITIMER_REAL, &timer, 0) == -1)
00126 {
00127 clear_alarm();
00128 throw error(TIMEOUT_SET, strerror(errno));
00129 }
00130 }
00131
00132 void
00133 lock::unset_timer ()
00134 {
00135 struct itimerval disable_timer;
00136 disable_timer.it_interval.tv_sec = disable_timer.it_interval.tv_usec = 0;
00137 disable_timer.it_value.tv_sec = disable_timer.it_value.tv_usec = 0;
00138
00139 if (setitimer(ITIMER_REAL, &disable_timer, 0) == -1)
00140 {
00141 clear_alarm();
00142 throw error(TIMEOUT_CANCEL, strerror(errno));
00143 }
00144
00145 clear_alarm();
00146 }
00147
00148 file_lock::file_lock (int fd):
00149 lock(),
00150 fd(fd)
00151 {
00152 }
00153
00154 file_lock::~file_lock ()
00155 {
00156 }
00157
00158 void
00159 file_lock::set_lock (lock::type lock_type,
00160 unsigned int timeout)
00161 {
00162 try
00163 {
00164 struct itimerval timeout_timer;
00165 timeout_timer.it_interval.tv_sec = timeout_timer.it_interval.tv_usec = 0;
00166 timeout_timer.it_value.tv_sec = timeout;
00167 timeout_timer.it_value.tv_usec = 0;
00168 set_timer(timeout_timer);
00169
00170
00171
00172
00173
00174
00175
00176 struct flock read_lock =
00177 {
00178 lock_type,
00179 SEEK_SET,
00180 0,
00181 0,
00182 0
00183 };
00184
00185 if (fcntl(this->fd,
00186 (timeout != 0) ? F_SETLKW : F_SETLK,
00187 &read_lock) == -1)
00188 {
00189 if (errno == EINTR)
00190 throw error(LOCK_TIMEOUT, timeout);
00191 else
00192 throw error(LOCK, strerror(errno));
00193 }
00194 unset_timer();
00195 }
00196 catch (error const& e)
00197 {
00198 unset_timer();
00199 throw;
00200 }
00201 }
00202
00203 void
00204 file_lock::unset_lock ()
00205 {
00206 set_lock(LOCK_NONE, 0);
00207 }
00208
00209 device_lock::device_lock (std::string const& device):
00210 lock(),
00211 device(device)
00212 {
00213 }
00214
00215 device_lock::~device_lock ()
00216 {
00217 }
00218
00219 void
00220 device_lock::set_lock (lock::type lock_type,
00221 unsigned int timeout)
00222 {
00223 try
00224 {
00225 lock_timeout = false;
00226
00227 struct itimerval timeout_timer;
00228 timeout_timer.it_interval.tv_sec = timeout_timer.it_interval.tv_usec = 0;
00229 timeout_timer.it_value.tv_sec = timeout;
00230 timeout_timer.it_value.tv_usec = 0;
00231 set_timer(timeout_timer);
00232
00233
00234
00235
00236
00237
00238
00239 pid_t status = 0;
00240 while (lock_timeout == false)
00241 {
00242 if (lock_type == LOCK_SHARED || lock_type == LOCK_EXCLUSIVE)
00243 {
00244 status = dev_lock(this->device.c_str());
00245 if (status == 0)
00246 break;
00247 else if (status < 0)
00248 {
00249 throw error(DEVICE_LOCK);
00250 }
00251 }
00252 else
00253 {
00254 pid_t cur_lock_pid = dev_testlock(this->device.c_str());
00255 if (cur_lock_pid < 0)
00256 {
00257 throw error(DEVICE_TEST);
00258 }
00259 else if (cur_lock_pid > 0 && cur_lock_pid != getpid())
00260 {
00261
00262
00263 break;
00264 }
00265 status = dev_unlock(this->device.c_str(), getpid());
00266 if (status == 0)
00267 break;
00268 else if (status < 0)
00269 {
00270 throw error(DEVICE_UNLOCK);
00271 }
00272 }
00273 }
00274
00275 if (lock_timeout)
00276 {
00277 throw error(((lock_type == LOCK_SHARED || lock_type == LOCK_EXCLUSIVE)
00278 ? DEVICE_LOCK_TIMEOUT : DEVICE_UNLOCK_TIMEOUT),
00279 timeout, status);
00280 }
00281 unset_timer();
00282 }
00283 catch (error const& e)
00284 {
00285 unset_timer();
00286 throw;
00287 }
00288 }
00289
00290 void
00291 device_lock::unset_lock ()
00292 {
00293 set_lock(LOCK_NONE, 0);
00294 }