sapling/eden/fs/notifications/Notifications.cpp
Wez Furlong 28f7b4a0b0 eden: show desktop notification for blob/tree fetch errors
Summary:
This commit causes a desktop notification to be shown if we generate
EIO or ETIMEDOUT responses via fuse; the prompt is intended to make it obvious
to the user that they need to connect to the VPN.

The commit by itself doesn't show a notification, it allows configuring a
command that can be run to do something to show a notification.

The test plan includes one such configuration for our corp environment.

* It doesn't trigger for thrift-originated downloads (eg: prefetch), only for
  VFS operations through FUSE.
* Ideally we'd know exactly when we have a network related error in the store
  code and use that to trigger the notification.  However, we have a rather
  convoluted set of importers and fallbacks today, one of which is interpreting
  a generic response returned from a pipe, so it is not especially clear
  exactly where we should locate the logic

Reviewed By: chadaustin

Differential Revision: D17513364

fbshipit-source-id: 45134f3672679cb5580cb0c1bc12a0d6e38525ca
2020-02-10 08:28:49 -08:00

64 lines
1.6 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#include "eden/fs/notifications/Notifications.h"
#include <folly/Subprocess.h>
#include <folly/futures/Future.h>
#include "eden/fs/config/EdenConfig.h"
#include "eden/fs/config/ReloadableConfig.h"
#include "eden/fs/utils/SystemError.h"
namespace facebook {
namespace eden {
bool Notifications::canShowNotification() {
auto now = std::chrono::steady_clock::now();
auto last = lastShown_.wlock();
if (*last) {
auto expiry = last->value() +
config_.getEdenConfig()->notificationInterval.getValue();
if (now < expiry) {
return false;
}
}
return true;
}
namespace {
bool isGenericConnectivityError(const std::exception& err) {
int errnum = EIO;
if (auto* sys = dynamic_cast<const std::system_error*>(&err)) {
if (isErrnoError(*sys)) {
errnum = sys->code().value();
}
} else if (auto* timeout = dynamic_cast<const folly::FutureTimeout*>(&err)) {
errnum = ETIMEDOUT;
}
return errnum == EIO || errnum == ETIMEDOUT;
}
} // namespace
void Notifications::showGenericErrorNotification(const std::exception& err) {
if (!isGenericConnectivityError(err)) {
return;
}
if (!canShowNotification()) {
return;
}
*lastShown_.wlock() = std::chrono::steady_clock::now();
folly::Subprocess proc(
{"/bin/sh",
"-c",
config_.getEdenConfig()->genericErrorNotificationCommand.getValue()},
folly::Subprocess::Options().detach());
}
} // namespace eden
} // namespace facebook