<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/webui_login_view.h"

#include <vector>

#include "base/logging.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
#include "chrome/browser/chromeos/status/clock_menu_button.h"
#include "chrome/browser/chromeos/status/input_method_menu_button.h"
#include "chrome/browser/chromeos/status/network_menu_button.h"
#include "chrome/browser/chromeos/status/status_area_view.h"
#include "chrome/browser/chromeos/wm_ipc.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/renderer_host/render_widget_host_view_views.h"
#include "chrome/browser/ui/touch/frame/keyboard_container_view.h"
#include "chrome/browser/ui/views/dom_view.h"
#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/transform.h"
#include "views/controls/textfield/textfield.h"
#include "views/widget/widget.h"

// TODO(rharrison): Modify this class to support both touch and non-touch

namespace {

const int kKeyboardHeight = 300;
const int kKeyboardSlideDuration = 500;  // In milliseconds

PropertyAccessor<bool>* GetFocusedStateAccessor() {
  static PropertyAccessor<bool> state;
  return &state;
}

bool TabContentsHasFocus(const TabContents* contents) {
  views::View* view = static_cast<TabContentsViewTouch*>(contents->view());
  return view->Contains(view->GetFocusManager()->GetFocusedView());
}

}  // namespace

namespace chromeos {

// static
const char WebUILoginView::kViewClassName[] =
    "browser/chromeos/login/WebUILoginView";

// WebUILoginView public: ------------------------------------------------------

WebUILoginView::WebUILoginView()
    : profile_(NULL),
      status_area_(NULL),
      webui_login_(NULL),
      keyboard_showing_(false),
      focus_listener_added_(false),
      keyboard_(NULL) {
}

void WebUILoginView::Init(const GURL& login_url) {
  CHECK(!login_url.is_empty());

  profile_ = ProfileManager::GetDefaultProfile();

  webui_login_ = new DOMView();
  AddChildView(webui_login_);
  webui_login_->Init(profile_, NULL);
  webui_login_->LoadURL(login_url);
  webui_login_->SetVisible(true);

  InitStatusArea();

  registrar_.Add(this,
                 NotificationType::FOCUS_CHANGED_IN_PAGE,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 NotificationType::TAB_CONTENTS_DESTROYED,
                 NotificationService::AllSources());
}

// static
views::Widget* WebUILoginView::CreateWindowContainingView(
    const gfx::Rect& bounds,
    const GURL& login_url,
    WebUILoginView** view) {
  views::Widget* window = new views::Widget;
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
  params.bounds = bounds;
  window->Init(params);
  *view = new WebUILoginView();
  (*view)->Init(login_url);

  window->SetContentsView(*view);

  (*view)->UpdateWindowType();

  return window;
}

std::string WebUILoginView::GetClassName() const {
  return kViewClassName;
}

gfx::NativeWindow WebUILoginView::GetNativeWindow() const {
  return GetWidget()->GetNativeWindow();
}

void WebUILoginView::FocusWillChange(views::View* focused_before,
                                     views::View* focused_now) {
  VirtualKeyboardType before = DecideKeyboardStateForView(focused_before);
  VirtualKeyboardType now = DecideKeyboardStateForView(focused_now);
  if (before != now) {
    // TODO(varunjain): support other types of keyboard.
    UpdateKeyboardAndLayout(now == GENERIC);
  }
}

// WebUILoginView protected: ---------------------------------------------------

void WebUILoginView::Layout() {
  const int kCornerPadding = 5;
  gfx::Size status_area_size = status_area_->GetPreferredSize();
  status_area_->SetBounds(
      width() - status_area_size.width() - kCornerPadding,
      kCornerPadding,
      status_area_size.width(),
      status_area_size.height());

  if (webui_login_)
    webui_login_->SetBoundsRect(bounds());

  // TODO(rharrison): Hide touch specific code behind TOUCH_UI defines
  if (!keyboard_)
    return;

  keyboard_->SetVisible(keyboard_showing_);
  gfx::Rect keyboard_bounds = bounds();
  keyboard_bounds.set_y(keyboard_bounds.height() - kKeyboardHeight);
  keyboard_bounds.set_height(kKeyboardHeight);
  keyboard_->SetBoundsRect(keyboard_bounds);
}

void WebUILoginView::ChildPreferredSizeChanged(View* child) {
  Layout();
  SchedulePaint();
}

Profile* WebUILoginView::GetProfile() const {
  return NULL;
}

void WebUILoginView::ExecuteBrowserCommand(int id) const {
}

bool WebUILoginView::ShouldOpenButtonOptions(
    const views::View* button_view) const {
  if (button_view == status_area_->network_view())
    return true;

  if (button_view == status_area_->clock_view() ||
      button_view == status_area_->input_method_view())
    return false;

  return true;
}

void WebUILoginView::OpenButtonOptions(const views::View* button_view) {
  if (button_view == status_area_->network_view()) {
    if (proxy_settings_dialog_.get() == NULL) {
      proxy_settings_dialog_.reset(new ProxySettingsDialog(
          this, GetNativeWindow()));
    }
    proxy_settings_dialog_->Show();
  }
}

StatusAreaHost::ScreenMode WebUILoginView::GetScreenMode() const {
  return kLoginMode;
}

StatusAreaHost::TextStyle WebUILoginView::GetTextStyle() const {
  return kWhitePlain;
}

void WebUILoginView::OnDialogClosed() {
}

void WebUILoginView::OnLocaleChanged() {
  // Proxy settings dialog contains localized strings.
  proxy_settings_dialog_.reset();
  SchedulePaint();
}

// WebUILoginView private: -----------------------------------------------------

void WebUILoginView::InitStatusArea() {
  DCHECK(status_area_ == NULL);
  status_area_ = new StatusAreaView(this);
  status_area_->Init();
  AddChildView(status_area_);
}

void WebUILoginView::UpdateWindowType() {
  std::vector<int> params;
  WmIpc::instance()->SetWindowType(
      GTK_WIDGET(GetNativeWindow()),
      WM_IPC_WINDOW_LOGIN_WEBUI,
      &params);
}

void WebUILoginView::InitVirtualKeyboard() {
  if (keyboard_)
    return;

  keyboard_ = new KeyboardContainerView(profile_, NULL);
  keyboard_->SetVisible(false);
  AddChildView(keyboard_);
}

void WebUILoginView::UpdateKeyboardAndLayout(bool should_show_keyboard) {
  if (should_show_keyboard)
    InitVirtualKeyboard();

  if (should_show_keyboard == keyboard_showing_)
    return;

  DCHECK(keyboard_);

  keyboard_showing_ = should_show_keyboard;
  Layout();
}

WebUILoginView::VirtualKeyboardType
    WebUILoginView::DecideKeyboardStateForView(views::View* view) {
  if (!view)
    return NONE;

  std::string cname = view->GetClassName();
  if (cname == views::Textfield::kViewClassName) {
    return GENERIC;
  } else if (cname == RenderWidgetHostViewViews::kViewClassName) {
    TabContents* contents = webui_login_->tab_contents();
    bool* editable = contents ? GetFocusedStateAccessor()->GetProperty(
        contents->property_bag()) : NULL;
    if (editable && *editable)
      return GENERIC;
  }
  return NONE;
}

void WebUILoginView::Observe(NotificationType type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  if (type == NotificationType::FOCUS_CHANGED_IN_PAGE) {
    // Only modify the keyboard state if the currently active tab sent the
    // notification.
    const TabContents* current_tab = webui_login_->tab_contents();
    TabContents* source_tab = Source<TabContents>(source).ptr();
    const bool editable = *Details<const bool>(details).ptr();

    if (current_tab == source_tab && TabContentsHasFocus(source_tab))
      UpdateKeyboardAndLayout(editable);

    // Save the state of the focused field so that the keyboard visibility
    // can be determined after tab switching.
    GetFocusedStateAccessor()->SetProperty(
        source_tab->property_bag(), editable);
  } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
    GetFocusedStateAccessor()->DeleteProperty(
        Source<TabContents>(source).ptr()->property_bag());
  }
}

}  // namespace chromeos
<commit_msg>Changed kKeyboardHeight in WebUILoginView to reflect change in TouchBrowserFrameView<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/webui_login_view.h"

#include <vector>

#include "base/logging.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
#include "chrome/browser/chromeos/status/clock_menu_button.h"
#include "chrome/browser/chromeos/status/input_method_menu_button.h"
#include "chrome/browser/chromeos/status/network_menu_button.h"
#include "chrome/browser/chromeos/status/status_area_view.h"
#include "chrome/browser/chromeos/wm_ipc.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/renderer_host/render_widget_host_view_views.h"
#include "chrome/browser/ui/touch/frame/keyboard_container_view.h"
#include "chrome/browser/ui/views/dom_view.h"
#include "chrome/browser/ui/views/tab_contents/tab_contents_view_touch.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "ui/base/x/x11_util.h"
#include "ui/gfx/transform.h"
#include "views/controls/textfield/textfield.h"
#include "views/widget/widget.h"

// TODO(rharrison): Modify this class to support both touch and non-touch

namespace {

const int kKeyboardHeight = 360;
const int kKeyboardSlideDuration = 500;  // In milliseconds

PropertyAccessor<bool>* GetFocusedStateAccessor() {
  static PropertyAccessor<bool> state;
  return &state;
}

bool TabContentsHasFocus(const TabContents* contents) {
  views::View* view = static_cast<TabContentsViewTouch*>(contents->view());
  return view->Contains(view->GetFocusManager()->GetFocusedView());
}

}  // namespace

namespace chromeos {

// static
const char WebUILoginView::kViewClassName[] =
    "browser/chromeos/login/WebUILoginView";

// WebUILoginView public: ------------------------------------------------------

WebUILoginView::WebUILoginView()
    : profile_(NULL),
      status_area_(NULL),
      webui_login_(NULL),
      keyboard_showing_(false),
      focus_listener_added_(false),
      keyboard_(NULL) {
}

void WebUILoginView::Init(const GURL& login_url) {
  CHECK(!login_url.is_empty());

  profile_ = ProfileManager::GetDefaultProfile();

  webui_login_ = new DOMView();
  AddChildView(webui_login_);
  webui_login_->Init(profile_, NULL);
  webui_login_->LoadURL(login_url);
  webui_login_->SetVisible(true);

  InitStatusArea();

  registrar_.Add(this,
                 NotificationType::FOCUS_CHANGED_IN_PAGE,
                 NotificationService::AllSources());
  registrar_.Add(this,
                 NotificationType::TAB_CONTENTS_DESTROYED,
                 NotificationService::AllSources());
}

// static
views::Widget* WebUILoginView::CreateWindowContainingView(
    const gfx::Rect& bounds,
    const GURL& login_url,
    WebUILoginView** view) {
  views::Widget* window = new views::Widget;
  views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
  params.bounds = bounds;
  window->Init(params);
  *view = new WebUILoginView();
  (*view)->Init(login_url);

  window->SetContentsView(*view);

  (*view)->UpdateWindowType();

  return window;
}

std::string WebUILoginView::GetClassName() const {
  return kViewClassName;
}

gfx::NativeWindow WebUILoginView::GetNativeWindow() const {
  return GetWidget()->GetNativeWindow();
}

void WebUILoginView::FocusWillChange(views::View* focused_before,
                                     views::View* focused_now) {
  VirtualKeyboardType before = DecideKeyboardStateForView(focused_before);
  VirtualKeyboardType now = DecideKeyboardStateForView(focused_now);
  if (before != now) {
    // TODO(varunjain): support other types of keyboard.
    UpdateKeyboardAndLayout(now == GENERIC);
  }
}

// WebUILoginView protected: ---------------------------------------------------

void WebUILoginView::Layout() {
  const int kCornerPadding = 5;
  gfx::Size status_area_size = status_area_->GetPreferredSize();
  status_area_->SetBounds(
      width() - status_area_size.width() - kCornerPadding,
      kCornerPadding,
      status_area_size.width(),
      status_area_size.height());

  if (webui_login_)
    webui_login_->SetBoundsRect(bounds());

  // TODO(rharrison): Hide touch specific code behind TOUCH_UI defines
  if (!keyboard_)
    return;

  keyboard_->SetVisible(keyboard_showing_);
  gfx::Rect keyboard_bounds = bounds();
  keyboard_bounds.set_y(keyboard_bounds.height() - kKeyboardHeight);
  keyboard_bounds.set_height(kKeyboardHeight);
  keyboard_->SetBoundsRect(keyboard_bounds);
}

void WebUILoginView::ChildPreferredSizeChanged(View* child) {
  Layout();
  SchedulePaint();
}

Profile* WebUILoginView::GetProfile() const {
  return NULL;
}

void WebUILoginView::ExecuteBrowserCommand(int id) const {
}

bool WebUILoginView::ShouldOpenButtonOptions(
    const views::View* button_view) const {
  if (button_view == status_area_->network_view())
    return true;

  if (button_view == status_area_->clock_view() ||
      button_view == status_area_->input_method_view())
    return false;

  return true;
}

void WebUILoginView::OpenButtonOptions(const views::View* button_view) {
  if (button_view == status_area_->network_view()) {
    if (proxy_settings_dialog_.get() == NULL) {
      proxy_settings_dialog_.reset(new ProxySettingsDialog(
          this, GetNativeWindow()));
    }
    proxy_settings_dialog_->Show();
  }
}

StatusAreaHost::ScreenMode WebUILoginView::GetScreenMode() const {
  return kLoginMode;
}

StatusAreaHost::TextStyle WebUILoginView::GetTextStyle() const {
  return kWhitePlain;
}

void WebUILoginView::OnDialogClosed() {
}

void WebUILoginView::OnLocaleChanged() {
  // Proxy settings dialog contains localized strings.
  proxy_settings_dialog_.reset();
  SchedulePaint();
}

// WebUILoginView private: -----------------------------------------------------

void WebUILoginView::InitStatusArea() {
  DCHECK(status_area_ == NULL);
  status_area_ = new StatusAreaView(this);
  status_area_->Init();
  AddChildView(status_area_);
}

void WebUILoginView::UpdateWindowType() {
  std::vector<int> params;
  WmIpc::instance()->SetWindowType(
      GTK_WIDGET(GetNativeWindow()),
      WM_IPC_WINDOW_LOGIN_WEBUI,
      &params);
}

void WebUILoginView::InitVirtualKeyboard() {
  if (keyboard_)
    return;

  keyboard_ = new KeyboardContainerView(profile_, NULL);
  keyboard_->SetVisible(false);
  AddChildView(keyboard_);
}

void WebUILoginView::UpdateKeyboardAndLayout(bool should_show_keyboard) {
  if (should_show_keyboard)
    InitVirtualKeyboard();

  if (should_show_keyboard == keyboard_showing_)
    return;

  DCHECK(keyboard_);

  keyboard_showing_ = should_show_keyboard;
  Layout();
}

WebUILoginView::VirtualKeyboardType
    WebUILoginView::DecideKeyboardStateForView(views::View* view) {
  if (!view)
    return NONE;

  std::string cname = view->GetClassName();
  if (cname == views::Textfield::kViewClassName) {
    return GENERIC;
  } else if (cname == RenderWidgetHostViewViews::kViewClassName) {
    TabContents* contents = webui_login_->tab_contents();
    bool* editable = contents ? GetFocusedStateAccessor()->GetProperty(
        contents->property_bag()) : NULL;
    if (editable && *editable)
      return GENERIC;
  }
  return NONE;
}

void WebUILoginView::Observe(NotificationType type,
                             const NotificationSource& source,
                             const NotificationDetails& details) {
  if (type == NotificationType::FOCUS_CHANGED_IN_PAGE) {
    // Only modify the keyboard state if the currently active tab sent the
    // notification.
    const TabContents* current_tab = webui_login_->tab_contents();
    TabContents* source_tab = Source<TabContents>(source).ptr();
    const bool editable = *Details<const bool>(details).ptr();

    if (current_tab == source_tab && TabContentsHasFocus(source_tab))
      UpdateKeyboardAndLayout(editable);

    // Save the state of the focused field so that the keyboard visibility
    // can be determined after tab switching.
    GetFocusedStateAccessor()->SetProperty(
        source_tab->property_bag(), editable);
  } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
    GetFocusedStateAccessor()->DeleteProperty(
        Source<TabContents>(source).ptr()->property_bag());
  }
}

}  // namespace chromeos
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: intro.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: hr $ $Date: 2000-09-18 16:52:34 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _SFX_INTRO_HXX
#define _SFX_INTRO_HXX

// include ---------------------------------------------------------------

#ifndef _SV_WRKWIN_HXX
#include <vcl/wrkwin.hxx>
#endif
#ifndef _SV_BITMAP_HXX
#include <vcl/bitmap.hxx>
#endif

// class IntroWindow_Impl ------------------------------------------------

class IntroWindow_Impl : public WorkWindow
{
private:
    Bitmap          aIntroBmp;

    void            Init();

public:
    IntroWindow_Impl( const Bitmap& rBmp );
    ~IntroWindow_Impl();

    virtual void    Paint( const Rectangle& );

    void            Slide();
};

#endif // #ifndef _SFX_INTRO_HXX

<commit_msg>INTEGRATION: CWS ooo19126 (1.1.1.1.1086); FILE MERGED 2005/09/06 13:05:31 rt 1.1.1.1.1086.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: intro.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 19:04:44 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _SFX_INTRO_HXX
#define _SFX_INTRO_HXX

// include ---------------------------------------------------------------

#ifndef _SV_WRKWIN_HXX
#include <vcl/wrkwin.hxx>
#endif
#ifndef _SV_BITMAP_HXX
#include <vcl/bitmap.hxx>
#endif

// class IntroWindow_Impl ------------------------------------------------

class IntroWindow_Impl : public WorkWindow
{
private:
    Bitmap          aIntroBmp;

    void            Init();

public:
    IntroWindow_Impl( const Bitmap& rBmp );
    ~IntroWindow_Impl();

    virtual void    Paint( const Rectangle& );

    void            Slide();
};

#endif // #ifndef _SFX_INTRO_HXX

<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkFixedWidthTextReader.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkFixedWidthTextReader.h"
#include "vtkCommand.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkInformation.h"
#include "vtkStringArray.h"
#include "vtkStdString.h"
#include "vtkIOStream.h"

#include <vtksys/stl/algorithm>
#include <vtksys/stl/vector>
#include <vtksys/ios/fstream>

#include <ctype.h>

vtkStandardNewMacro(vtkFixedWidthTextReader);

// Function body at bottom of file
static int splitString(const vtkStdString& input,
                       unsigned int fieldWidth,
                       bool stripWhitespace,
                       vtksys_stl::vector<vtkStdString>& results,
                       bool includeEmpties=true);

// I need a safe way to read a line of arbitrary length.  It exists on
// some platforms but not others so I'm afraid I have to write it
// myself.
static int my_getline(istream& stream, vtkStdString &output, char delim='\n');

// ----------------------------------------------------------------------

vtkFixedWidthTextReader::vtkFixedWidthTextReader()
{
  this->FileName = NULL;
  this->StripWhiteSpace = false;
  this->HaveHeaders = false;
  this->FieldWidth = 10;
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
}

// ----------------------------------------------------------------------

vtkFixedWidthTextReader::~vtkFixedWidthTextReader()
{
  this->SetFileName(0);
}

// ----------------------------------------------------------------------

void vtkFixedWidthTextReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "FileName: "
     << (this->FileName ? this->FileName : "(none)") << endl;
  os << indent << "Field width: " << this->FieldWidth << endl;
  os << indent << "Strip leading/trailing whitespace: "
     << (this->StripWhiteSpace ? "Yes" : "No") << endl;
  os << indent << "HaveHeaders: "
     << (this->HaveHeaders ? "Yes" : "No") << endl;
}

// ----------------------------------------------------------------------

int vtkFixedWidthTextReader::RequestData(
                                        vtkInformation*,
                                        vtkInformationVector**,
                                        vtkInformationVector* outputVector)
{
  int numLines = 0;

  // Check that the filename has been specified
  if (!this->FileName)
    {
    vtkErrorMacro("vtkFixedWidthTextReader: You must specify a filename!");
    return 2;
    }

  vtksys_ios::ifstream infile(this->FileName, ios::in);
  if (!infile || infile.fail())
    {
    vtkErrorMacro(<<"vtkFixedWidthTextReader: Couldn't open file!");
    return 2;
    }

  // The first line of the file might contain the headers, so we want
  // to be a little bit careful about it.  If we don't have headers
  // we'll have to make something up.
  vtksys_stl::vector<vtkStdString> headers;
  vtksys_stl::vector<vtkStdString> firstLineFields;
  vtkStdString firstLine;

  my_getline(infile, firstLine);

//  vtkDebugMacro(<<"First line of file: " << firstLine.c_str());

  if (this->HaveHeaders)
    {
    splitString(firstLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                headers);
    }
  else
    {
    splitString(firstLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                firstLineFields);

    for (unsigned int i = 0; i < firstLineFields.size(); ++i)
      {
      // I know it's not a great idea to use sprintf.  It's safe right
      // here because an unsigned int will never take up enough
      // characters to fill up this buffer.
      char fieldName[64];
      sprintf(fieldName, "Field %d", i);
      headers.push_back(fieldName);
      }
    }

  vtkTable *table = vtkTable::GetData(outputVector);

  // Now we can create the arrays that will hold the data for each
  // field.
  vtksys_stl::vector<vtkStdString>::const_iterator fieldIter;
  for(fieldIter = headers.begin(); fieldIter != headers.end(); ++fieldIter)
    {
    vtkStringArray* array = vtkStringArray::New();
    array->SetName((*fieldIter).c_str());
    table->AddColumn(array);
    array->Delete();
    }

  // If the first line did not contain headers then we need to add it
  // to the table.
  if (!this->HaveHeaders)
    {
    vtkVariantArray* dataArray = vtkVariantArray::New();
    vtksys_stl::vector<vtkStdString>::const_iterator I;
    for(I = firstLineFields.begin(); I != firstLineFields.end(); ++I)
      {
      dataArray->InsertNextValue(vtkVariant(*I));
      }

    // Insert the data into the table
    table->InsertNextRow(dataArray);
    dataArray->Delete();
    }

  // Read the file line-by-line and add it to the table.
  vtkStdString nextLine;
  while (my_getline(infile, nextLine))
    {
    ++numLines;
    if (numLines > 0 && numLines % 100 == 0)
      {
      float numLinesRead = numLines;
      this->InvokeEvent(vtkCommand::ProgressEvent, &numLinesRead);
      }

    vtkDebugMacro(<<"Next line: " << nextLine.c_str());
    vtksys_stl::vector<vtkStdString> dataVector;

    // Split string on the delimiters
    splitString(nextLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                dataVector);

    vtkDebugMacro(<<"Split into " << dataVector.size() << " fields");
    // Add data to the output arrays

    // Convert from vector to variant array
    vtkVariantArray* dataArray = vtkVariantArray::New();
    vtksys_stl::vector<vtkStdString>::const_iterator I;
    for(I = dataVector.begin(); I != dataVector.end(); ++I)
      {
      dataArray->InsertNextValue(vtkVariant(*I));
      }

    // Pad out any missing columns
    while (dataArray->GetNumberOfTuples() < table->GetNumberOfColumns())
      {
      dataArray->InsertNextValue(vtkVariant());
      }

    // Insert the data into the table
    table->InsertNextRow(dataArray);
    dataArray->Delete();
    }

  infile.close();

  return 1;
}

// ----------------------------------------------------------------------

static int
splitString(const vtkStdString& input,
            unsigned int fieldWidth,
            bool stripWhitespace,
            vtksys_stl::vector<vtkStdString>& results,
            bool includeEmpties)
{
  if (input.size() == 0)
    {
    return 0;
    }

  unsigned int thisField = 0;
  vtkStdString thisFieldText;
  vtkStdString parsedField;

  while (thisField * fieldWidth < input.size())
    {
    thisFieldText = input.substr(thisField*fieldWidth, fieldWidth);

    if (stripWhitespace)
      {
      unsigned int startIndex = 0, endIndex = static_cast<unsigned int>(thisFieldText.size()) - 1;
      while (startIndex < thisFieldText.size() &&
             isspace(static_cast<int>(thisFieldText.at(startIndex))))
        {
        ++startIndex;
        }
      while (endIndex > 0 &&
             isspace(static_cast<int>(thisFieldText.at(endIndex))))
        {
        -- endIndex;
        }

      if (startIndex <= endIndex)
        {
        parsedField =
          thisFieldText.substr(startIndex, (endIndex - startIndex) + 1);
        }
      else
        {
        parsedField = vtkStdString();
        }
      }
    else
      {
      parsedField = thisFieldText;
      }
    ++ thisField;
    if (parsedField.size() > 0 || includeEmpties)
      {
      results.push_back(parsedField);
      }
    }

  return static_cast<int>(results.size());
}

// ----------------------------------------------------------------------

static int
my_getline(istream& in, vtkStdString &out, char delimiter)
{
  out = vtkStdString();
  unsigned int numCharactersRead = 0;
  int nextValue = 0;

  while ((nextValue = in.get()) != EOF &&
         numCharactersRead < out.max_size())
    {
    ++numCharactersRead;

    char downcast = static_cast<char>(nextValue);
    if (downcast != delimiter && downcast != 0x0d)
      {
      out += downcast;
      }
    else
      {
      return numCharactersRead;
      }
    }

  return numCharactersRead;
}

<commit_msg>Fixed minor format string mismatch<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkFixedWidthTextReader.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/*-------------------------------------------------------------------------
  Copyright 2008 Sandia Corporation.
  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
  the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------*/

#include "vtkFixedWidthTextReader.h"
#include "vtkCommand.h"
#include "vtkTable.h"
#include "vtkVariantArray.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkInformation.h"
#include "vtkStringArray.h"
#include "vtkStdString.h"
#include "vtkIOStream.h"

#include <vtksys/stl/algorithm>
#include <vtksys/stl/vector>
#include <vtksys/ios/fstream>

#include <ctype.h>

vtkStandardNewMacro(vtkFixedWidthTextReader);

// Function body at bottom of file
static int splitString(const vtkStdString& input,
                       unsigned int fieldWidth,
                       bool stripWhitespace,
                       vtksys_stl::vector<vtkStdString>& results,
                       bool includeEmpties=true);

// I need a safe way to read a line of arbitrary length.  It exists on
// some platforms but not others so I'm afraid I have to write it
// myself.
static int my_getline(istream& stream, vtkStdString &output, char delim='\n');

// ----------------------------------------------------------------------

vtkFixedWidthTextReader::vtkFixedWidthTextReader()
{
  this->FileName = NULL;
  this->StripWhiteSpace = false;
  this->HaveHeaders = false;
  this->FieldWidth = 10;
  this->SetNumberOfInputPorts(0);
  this->SetNumberOfOutputPorts(1);
}

// ----------------------------------------------------------------------

vtkFixedWidthTextReader::~vtkFixedWidthTextReader()
{
  this->SetFileName(0);
}

// ----------------------------------------------------------------------

void vtkFixedWidthTextReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  os << indent << "FileName: "
     << (this->FileName ? this->FileName : "(none)") << endl;
  os << indent << "Field width: " << this->FieldWidth << endl;
  os << indent << "Strip leading/trailing whitespace: "
     << (this->StripWhiteSpace ? "Yes" : "No") << endl;
  os << indent << "HaveHeaders: "
     << (this->HaveHeaders ? "Yes" : "No") << endl;
}

// ----------------------------------------------------------------------

int vtkFixedWidthTextReader::RequestData(
                                        vtkInformation*,
                                        vtkInformationVector**,
                                        vtkInformationVector* outputVector)
{
  int numLines = 0;

  // Check that the filename has been specified
  if (!this->FileName)
    {
    vtkErrorMacro("vtkFixedWidthTextReader: You must specify a filename!");
    return 2;
    }

  vtksys_ios::ifstream infile(this->FileName, ios::in);
  if (!infile || infile.fail())
    {
    vtkErrorMacro(<<"vtkFixedWidthTextReader: Couldn't open file!");
    return 2;
    }

  // The first line of the file might contain the headers, so we want
  // to be a little bit careful about it.  If we don't have headers
  // we'll have to make something up.
  vtksys_stl::vector<vtkStdString> headers;
  vtksys_stl::vector<vtkStdString> firstLineFields;
  vtkStdString firstLine;

  my_getline(infile, firstLine);

//  vtkDebugMacro(<<"First line of file: " << firstLine.c_str());

  if (this->HaveHeaders)
    {
    splitString(firstLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                headers);
    }
  else
    {
    splitString(firstLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                firstLineFields);

    for (unsigned int i = 0; i < firstLineFields.size(); ++i)
      {
      // I know it's not a great idea to use sprintf.  It's safe right
      // here because an unsigned int will never take up enough
      // characters to fill up this buffer.
      char fieldName[64];
      sprintf(fieldName, "Field %u", i);
      headers.push_back(fieldName);
      }
    }

  vtkTable *table = vtkTable::GetData(outputVector);

  // Now we can create the arrays that will hold the data for each
  // field.
  vtksys_stl::vector<vtkStdString>::const_iterator fieldIter;
  for(fieldIter = headers.begin(); fieldIter != headers.end(); ++fieldIter)
    {
    vtkStringArray* array = vtkStringArray::New();
    array->SetName((*fieldIter).c_str());
    table->AddColumn(array);
    array->Delete();
    }

  // If the first line did not contain headers then we need to add it
  // to the table.
  if (!this->HaveHeaders)
    {
    vtkVariantArray* dataArray = vtkVariantArray::New();
    vtksys_stl::vector<vtkStdString>::const_iterator I;
    for(I = firstLineFields.begin(); I != firstLineFields.end(); ++I)
      {
      dataArray->InsertNextValue(vtkVariant(*I));
      }

    // Insert the data into the table
    table->InsertNextRow(dataArray);
    dataArray->Delete();
    }

  // Read the file line-by-line and add it to the table.
  vtkStdString nextLine;
  while (my_getline(infile, nextLine))
    {
    ++numLines;
    if (numLines > 0 && numLines % 100 == 0)
      {
      float numLinesRead = numLines;
      this->InvokeEvent(vtkCommand::ProgressEvent, &numLinesRead);
      }

    vtkDebugMacro(<<"Next line: " << nextLine.c_str());
    vtksys_stl::vector<vtkStdString> dataVector;

    // Split string on the delimiters
    splitString(nextLine,
                this->FieldWidth,
                this->StripWhiteSpace,
                dataVector);

    vtkDebugMacro(<<"Split into " << dataVector.size() << " fields");
    // Add data to the output arrays

    // Convert from vector to variant array
    vtkVariantArray* dataArray = vtkVariantArray::New();
    vtksys_stl::vector<vtkStdString>::const_iterator I;
    for(I = dataVector.begin(); I != dataVector.end(); ++I)
      {
      dataArray->InsertNextValue(vtkVariant(*I));
      }

    // Pad out any missing columns
    while (dataArray->GetNumberOfTuples() < table->GetNumberOfColumns())
      {
      dataArray->InsertNextValue(vtkVariant());
      }

    // Insert the data into the table
    table->InsertNextRow(dataArray);
    dataArray->Delete();
    }

  infile.close();

  return 1;
}

// ----------------------------------------------------------------------

static int
splitString(const vtkStdString& input,
            unsigned int fieldWidth,
            bool stripWhitespace,
            vtksys_stl::vector<vtkStdString>& results,
            bool includeEmpties)
{
  if (input.size() == 0)
    {
    return 0;
    }

  unsigned int thisField = 0;
  vtkStdString thisFieldText;
  vtkStdString parsedField;

  while (thisField * fieldWidth < input.size())
    {
    thisFieldText = input.substr(thisField*fieldWidth, fieldWidth);

    if (stripWhitespace)
      {
      unsigned int startIndex = 0, endIndex = static_cast<unsigned int>(thisFieldText.size()) - 1;
      while (startIndex < thisFieldText.size() &&
             isspace(static_cast<int>(thisFieldText.at(startIndex))))
        {
        ++startIndex;
        }
      while (endIndex > 0 &&
             isspace(static_cast<int>(thisFieldText.at(endIndex))))
        {
        -- endIndex;
        }

      if (startIndex <= endIndex)
        {
        parsedField =
          thisFieldText.substr(startIndex, (endIndex - startIndex) + 1);
        }
      else
        {
        parsedField = vtkStdString();
        }
      }
    else
      {
      parsedField = thisFieldText;
      }
    ++ thisField;
    if (parsedField.size() > 0 || includeEmpties)
      {
      results.push_back(parsedField);
      }
    }

  return static_cast<int>(results.size());
}

// ----------------------------------------------------------------------

static int
my_getline(istream& in, vtkStdString &out, char delimiter)
{
  out = vtkStdString();
  unsigned int numCharactersRead = 0;
  int nextValue = 0;

  while ((nextValue = in.get()) != EOF &&
         numCharactersRead < out.max_size())
    {
    ++numCharactersRead;

    char downcast = static_cast<char>(nextValue);
    if (downcast != delimiter && downcast != 0x0d)
      {
      out += downcast;
      }
    else
      {
      return numCharactersRead;
      }
    }

  return numCharactersRead;
}

<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2020, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_config.h"
#include "match_prefix.h"
#include <termios.h>
#include <unistd.h>
#include <grp.h>
#include <signal.h>
#include <algorithm>

// condor_nsenter
//
// This is a replacement for the linux nsenter command to launch a
// shell inside a container.  We need this when running
// condor_ssh_to_job to a job that has been launched inside singularity
// Docker jobs use docker exec to enter the container, but there is no
// equivalent in singularity.  Standard nsenter isn't sufficient, as it
// does not create a pty for the shell, and we need different command
// line arguments to enter a non-setuid singularity container.  This
// is harder because the starter can't tell if singularity is setuid easily.
//
// The architecture for ssh-to-job to a contained job is to land in an sshd
// the starter forks *outside* the container.  This is important because we
// never want to assume anything about the container, even that there is an sshd
// inside it.  After the sshd starts, a script runs which runs condor_docker_enter
// (even for singularity), which connects to a Unix Domain Socket, passes
// stdin/out/err to the starter, which passes those to condor_nsenter, which
// is runs as root.  Rootly privilege is required to enter a setuid namespace
// so this is how we acquire.
//
// condor_nsenter enters the namespace, taking care to try to enter the user
// namespace, which is only set up for non-setuid singularity, and drops
// privileges to the uid and gid passed on the command line.  It sets up
// a pty for the shell to use, so all interactive processses will work.
//
// A final problem is environment variables.  Singularity, especially when
// managing GPUs, sets some nvidia-related environment variables that the
// condor starter doesn't know about.  So, condor_nsenter extracts the environment
// variables from the contained job process, and sets them in the shell
// it spawns.

void
usage( char *cmd )
{
	fprintf(stderr,"Usage: %s [options] condor_* ....\n",cmd);
	fprintf(stderr,"Where options are:\n");
	fprintf(stderr,"-t target_pid:\n");
	fprintf(stderr,"-S user_id:\n");
	fprintf(stderr,"-G group_id:\n");
	fprintf(stderr,"    -help              Display options\n");
}

// Before we exit, we need to reset the pty back to normal
// if we put it in raw mode

bool pty_is_raw = false;
struct termios old_tio;
void reset_pty_and_exit(int signo) {
	if (pty_is_raw)
		tcsetattr(0, TCSAFLUSH, &old_tio);
	exit(signo);
}

int main( int argc, char *argv[] )
{
	std::string condor_prefix;
	pid_t pid = 0;
	uid_t uid = 0;
	gid_t gid = 0;

	// parse command line args
	for( int i=1; i<argc; i++ ) {
		if(is_arg_prefix(argv[i],"-help")) {
			usage(argv[0]);
			exit(1);
		}

		// target pid to enter
		if(is_arg_prefix(argv[i],"-t")) {
			pid = atoi(argv[i + 1]);
			i++;
		}

		// uid to switch to
		if(is_arg_prefix(argv[i],"-S")) {
			uid = atoi(argv[i + 1]);
			i++;
		}

		// gid to switch to
		if(is_arg_prefix(argv[i],"-G")) {
			gid = atoi(argv[i + 1]);
			i++;
		}
	}

	if (pid < 1) {
		fprintf(stderr, "missing -t argument > 1\n");
		exit(1);
	}

	if (uid == 0) {
		fprintf(stderr, "missing -S argument > 1\n");
		exit(1);
	}

	if (gid == 0) {
		fprintf(stderr, "missing -G argument > 1\n");
		exit(1);
	}

	// slurp the enviroment out of our victim
	std::string env;
	std::string envfile;
	formatstr(envfile, "/proc/%d/environ", pid);

	int e = open(envfile.c_str(), O_RDONLY);
	if (e < 0) {
		fprintf(stderr, "Can't open %s %s\n", envfile.c_str(), strerror(errno));
		exit(1);
	}
	char buf[512];
	int bytesRead;
	while ((bytesRead = read(e, &buf, 512)) > 0) {
		env.append(buf, bytesRead);
	}
	close(e);

	// make a vector to hold all the pointers to env entries
	std::vector<const char *> envp;

	// the first one
	envp.push_back(env.c_str());
	auto it = env.cbegin();
	while (env.cend() != (it = std::find(it, env.cend(), '\0'))) {
		// skip past null terminator
		it++;
		if (& (*it)  != nullptr) {
			envp.push_back(& (*it));
		}
	}
	envp.push_back(nullptr);
	envp.push_back(nullptr);

	// grab the fd for the cwd -- need to get this outside
	// but chdir inside the container
	std::string cwdPath;
	formatstr(cwdPath, "/proc/%d/cwd", pid);
	int rootFd = open(cwdPath.c_str(), O_RDONLY);
	if (rootFd < 0) {
		fprintf(stderr, "Can't open %s\n", cwdPath.c_str());
	}

	std::string filename;

	// start changing namespaces.  Note that once we do this, things
	// get funny in this process
	formatstr(filename, "/proc/%d/ns/uts", pid);
	int fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open uts namespace: %d %s\n", errno, strerror(errno));
		exit(1);
	}
	int r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		// This means an unprivileged singularity, most likely
		// need to set user namespace instead.
		formatstr(filename, "/proc/%d/ns/user", pid);
		fd = open(filename.c_str(), O_RDONLY);
		if (fd < 0) {
			fprintf(stderr, "Can't open user namespace: %d %s\n", errno, strerror(errno));
			exit(1);
		}
		r = setns(fd, 0);
		close(fd);
		if (r < 0) {
			fprintf(stderr, "Can't setns to user namespace: %s\n", strerror(errno));
			exit(1);
		}
	}

	// now the pid namespace
	formatstr(filename, "/proc/%d/ns/pid", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open pid namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to pid namespace: %s\n", strerror(errno));
		exit(1);
	}

	// finally the mnt namespace
	formatstr(filename, "/proc/%d/ns/mnt", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open mnt namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to mnt namespace: %s\n", strerror(errno));
		exit(1);
	}

	setgroups(0, nullptr);

	// order matters!
	r = setgid(gid);
	if (r < 0) {
		fprintf(stderr, "Can't setgid to %d\n", gid);
		exit(1);
	}
	r = setuid(uid);
	if (r < 0) {
		fprintf(stderr, "Can't setuid to %d\n", uid);
		exit(1);
	}

	struct winsize win;
	ioctl(0, TIOCGWINSZ, &win);

	// now the pty handling
	int masterPty = -1;
	int workerPty = -1;
	masterPty = open("/dev/ptmx", O_RDWR);
	unlockpt(masterPty);

	if (masterPty < 0) {
		fprintf(stderr, "Can't open master pty %s\n", strerror(errno));
		exit(1);
	} else {
		workerPty = open(ptsname(masterPty), O_RDWR);
		if (workerPty < 0) {
			fprintf(stderr, "Can't open worker pty %s\n", strerror(errno));
			exit(1);
		}
	}
	int childpid = fork();
	if (childpid == 0) {

		// in the child --

		close(0);
		close(1);
		close(2);
		close(masterPty);

		dup2(workerPty, 0);
		dup2(workerPty, 1);
		dup2(workerPty, 2);

		// chdir to existing cwd
		int ret = fchdir(rootFd);
		if (ret < 0) {
			fprintf(stderr, "Can't open %s\n", cwdPath.c_str());
		}
		close(rootFd);

		// make this process group leader so shell job control works
		setsid();

		// Make the pty the controlling terminal
		ioctl(workerPty, TIOCSCTTY, 0);

		// and make it the process group leader
		tcsetpgrp(workerPty, getpid());

		// and set the window size properly
		ioctl(0, TIOCSWINSZ, &win);

		// Finally, launch the shell
		execle("/bin/sh", "/bin/sh", "-l", "-i", nullptr, envp.data());

		// Only get here if exec fails
		fprintf(stderr, "exec failed %d\n", errno);
		exit(errno);

	} else {

		// the parent
		fd_set readfds, writefds, exceptfds;
		bool keepGoing = true;

		// put the pty in raw mode
		struct termios tio;
		tcgetattr(0, &tio);
		pty_is_raw = true;
		old_tio = tio;
		tio.c_oflag &= ~(OPOST);
		tio.c_cflag |= (CS8);
		tio.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
		tio.c_cc[VMIN] = 1;
		tio.c_cc[VTIME] = 0;
		tcsetattr(0, TCSAFLUSH, &tio);

		struct sigaction handler;
		struct sigaction oldhandler;
		handler.sa_handler = reset_pty_and_exit;
		handler.sa_flags   = 0;

		sigaction(SIGCHLD, &handler, &oldhandler);

		while (keepGoing) {
			FD_ZERO(&readfds);
			FD_ZERO(&writefds);
			FD_ZERO(&exceptfds);

			FD_SET(0, &readfds);
			FD_SET(masterPty, &readfds);

			select(masterPty + 1, &readfds, &writefds, &exceptfds, nullptr);

			if (FD_ISSET(masterPty, &readfds)) {
				char buf;
				int r = read(masterPty, &buf, 1);
				if (r > 0) {
					int ret = write(1, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}

			if (FD_ISSET(0, &readfds)) {
				char buf;
				int r = read(0, &buf, 1);
				if (r > 0) {
					int ret = write(masterPty, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}
		}
		int status;
		waitpid(childpid, &status, 0);
	}
	return 0;
}

<commit_msg>Fully initialize signal handler #6992<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2020, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_config.h"
#include "match_prefix.h"
#include <termios.h>
#include <unistd.h>
#include <grp.h>
#include <signal.h>
#include <algorithm>

// condor_nsenter
//
// This is a replacement for the linux nsenter command to launch a
// shell inside a container.  We need this when running
// condor_ssh_to_job to a job that has been launched inside singularity
// Docker jobs use docker exec to enter the container, but there is no
// equivalent in singularity.  Standard nsenter isn't sufficient, as it
// does not create a pty for the shell, and we need different command
// line arguments to enter a non-setuid singularity container.  This
// is harder because the starter can't tell if singularity is setuid easily.
//
// The architecture for ssh-to-job to a contained job is to land in an sshd
// the starter forks *outside* the container.  This is important because we
// never want to assume anything about the container, even that there is an sshd
// inside it.  After the sshd starts, a script runs which runs condor_docker_enter
// (even for singularity), which connects to a Unix Domain Socket, passes
// stdin/out/err to the starter, which passes those to condor_nsenter, which
// is runs as root.  Rootly privilege is required to enter a setuid namespace
// so this is how we acquire.
//
// condor_nsenter enters the namespace, taking care to try to enter the user
// namespace, which is only set up for non-setuid singularity, and drops
// privileges to the uid and gid passed on the command line.  It sets up
// a pty for the shell to use, so all interactive processses will work.
//
// A final problem is environment variables.  Singularity, especially when
// managing GPUs, sets some nvidia-related environment variables that the
// condor starter doesn't know about.  So, condor_nsenter extracts the environment
// variables from the contained job process, and sets them in the shell
// it spawns.

void
usage( char *cmd )
{
	fprintf(stderr,"Usage: %s [options] condor_* ....\n",cmd);
	fprintf(stderr,"Where options are:\n");
	fprintf(stderr,"-t target_pid:\n");
	fprintf(stderr,"-S user_id:\n");
	fprintf(stderr,"-G group_id:\n");
	fprintf(stderr,"    -help              Display options\n");
}

// Before we exit, we need to reset the pty back to normal
// if we put it in raw mode

bool pty_is_raw = false;
struct termios old_tio;
void reset_pty_and_exit(int signo) {
	if (pty_is_raw)
		tcsetattr(0, TCSAFLUSH, &old_tio);
	exit(signo);
}

int main( int argc, char *argv[] )
{
	std::string condor_prefix;
	pid_t pid = 0;
	uid_t uid = 0;
	gid_t gid = 0;

	// parse command line args
	for( int i=1; i<argc; i++ ) {
		if(is_arg_prefix(argv[i],"-help")) {
			usage(argv[0]);
			exit(1);
		}

		// target pid to enter
		if(is_arg_prefix(argv[i],"-t")) {
			pid = atoi(argv[i + 1]);
			i++;
		}

		// uid to switch to
		if(is_arg_prefix(argv[i],"-S")) {
			uid = atoi(argv[i + 1]);
			i++;
		}

		// gid to switch to
		if(is_arg_prefix(argv[i],"-G")) {
			gid = atoi(argv[i + 1]);
			i++;
		}
	}

	if (pid < 1) {
		fprintf(stderr, "missing -t argument > 1\n");
		exit(1);
	}

	if (uid == 0) {
		fprintf(stderr, "missing -S argument > 1\n");
		exit(1);
	}

	if (gid == 0) {
		fprintf(stderr, "missing -G argument > 1\n");
		exit(1);
	}

	// slurp the enviroment out of our victim
	std::string env;
	std::string envfile;
	formatstr(envfile, "/proc/%d/environ", pid);

	int e = open(envfile.c_str(), O_RDONLY);
	if (e < 0) {
		fprintf(stderr, "Can't open %s %s\n", envfile.c_str(), strerror(errno));
		exit(1);
	}
	char buf[512];
	int bytesRead;
	while ((bytesRead = read(e, &buf, 512)) > 0) {
		env.append(buf, bytesRead);
	}
	close(e);

	// make a vector to hold all the pointers to env entries
	std::vector<const char *> envp;

	// the first one
	envp.push_back(env.c_str());
	auto it = env.cbegin();
	while (env.cend() != (it = std::find(it, env.cend(), '\0'))) {
		// skip past null terminator
		it++;
		if (& (*it)  != nullptr) {
			envp.push_back(& (*it));
		}
	}
	envp.push_back(nullptr);
	envp.push_back(nullptr);

	// grab the fd for the cwd -- need to get this outside
	// but chdir inside the container
	std::string cwdPath;
	formatstr(cwdPath, "/proc/%d/cwd", pid);
	int rootFd = open(cwdPath.c_str(), O_RDONLY);
	if (rootFd < 0) {
		fprintf(stderr, "Can't open %s\n", cwdPath.c_str());
	}

	std::string filename;

	// start changing namespaces.  Note that once we do this, things
	// get funny in this process
	formatstr(filename, "/proc/%d/ns/uts", pid);
	int fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open uts namespace: %d %s\n", errno, strerror(errno));
		exit(1);
	}
	int r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		// This means an unprivileged singularity, most likely
		// need to set user namespace instead.
		formatstr(filename, "/proc/%d/ns/user", pid);
		fd = open(filename.c_str(), O_RDONLY);
		if (fd < 0) {
			fprintf(stderr, "Can't open user namespace: %d %s\n", errno, strerror(errno));
			exit(1);
		}
		r = setns(fd, 0);
		close(fd);
		if (r < 0) {
			fprintf(stderr, "Can't setns to user namespace: %s\n", strerror(errno));
			exit(1);
		}
	}

	// now the pid namespace
	formatstr(filename, "/proc/%d/ns/pid", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open pid namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to pid namespace: %s\n", strerror(errno));
		exit(1);
	}

	// finally the mnt namespace
	formatstr(filename, "/proc/%d/ns/mnt", pid);
	fd = open(filename.c_str(), O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Can't open mnt namespace: %s\n", strerror(errno));
		exit(1);
	}
	r = setns(fd, 0);
	close(fd);
	if (r < 0) {
		fprintf(stderr, "Can't setns to mnt namespace: %s\n", strerror(errno));
		exit(1);
	}

	setgroups(0, nullptr);

	// order matters!
	r = setgid(gid);
	if (r < 0) {
		fprintf(stderr, "Can't setgid to %d\n", gid);
		exit(1);
	}
	r = setuid(uid);
	if (r < 0) {
		fprintf(stderr, "Can't setuid to %d\n", uid);
		exit(1);
	}

	struct winsize win;
	ioctl(0, TIOCGWINSZ, &win);

	// now the pty handling
	int masterPty = -1;
	int workerPty = -1;
	masterPty = open("/dev/ptmx", O_RDWR);
	unlockpt(masterPty);

	if (masterPty < 0) {
		fprintf(stderr, "Can't open master pty %s\n", strerror(errno));
		exit(1);
	} else {
		workerPty = open(ptsname(masterPty), O_RDWR);
		if (workerPty < 0) {
			fprintf(stderr, "Can't open worker pty %s\n", strerror(errno));
			exit(1);
		}
	}
	int childpid = fork();
	if (childpid == 0) {

		// in the child --

		close(0);
		close(1);
		close(2);
		close(masterPty);

		dup2(workerPty, 0);
		dup2(workerPty, 1);
		dup2(workerPty, 2);

		// chdir to existing cwd
		int ret = fchdir(rootFd);
		if (ret < 0) {
			fprintf(stderr, "Can't open %s\n", cwdPath.c_str());
		}
		close(rootFd);

		// make this process group leader so shell job control works
		setsid();

		// Make the pty the controlling terminal
		ioctl(workerPty, TIOCSCTTY, 0);

		// and make it the process group leader
		tcsetpgrp(workerPty, getpid());

		// and set the window size properly
		ioctl(0, TIOCSWINSZ, &win);

		// Finally, launch the shell
		execle("/bin/sh", "/bin/sh", "-l", "-i", nullptr, envp.data());

		// Only get here if exec fails
		fprintf(stderr, "exec failed %d\n", errno);
		exit(errno);

	} else {

		// the parent
		fd_set readfds, writefds, exceptfds;
		bool keepGoing = true;

		// put the pty in raw mode
		struct termios tio;
		tcgetattr(0, &tio);
		pty_is_raw = true;
		old_tio = tio;
		tio.c_oflag &= ~(OPOST);
		tio.c_cflag |= (CS8);
		tio.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
		tio.c_cc[VMIN] = 1;
		tio.c_cc[VTIME] = 0;
		tcsetattr(0, TCSAFLUSH, &tio);

		struct sigaction handler;
		struct sigaction oldhandler;
		handler.sa_handler = reset_pty_and_exit;
		handler.sa_flags   = 0;
		sigemptyset(&handler.sa_mask);

		sigaction(SIGCHLD, &handler, &oldhandler);

		while (keepGoing) {
			FD_ZERO(&readfds);
			FD_ZERO(&writefds);
			FD_ZERO(&exceptfds);

			FD_SET(0, &readfds);
			FD_SET(masterPty, &readfds);

			select(masterPty + 1, &readfds, &writefds, &exceptfds, nullptr);

			if (FD_ISSET(masterPty, &readfds)) {
				char buf;
				int r = read(masterPty, &buf, 1);
				if (r > 0) {
					int ret = write(1, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}

			if (FD_ISSET(0, &readfds)) {
				char buf;
				int r = read(0, &buf, 1);
				if (r > 0) {
					int ret = write(masterPty, &buf, 1);
					if (ret < 0) {
						reset_pty_and_exit(0);
					}
				} else {
					keepGoing = false;
				}
			}
		}
		int status;
		waitpid(childpid, &status, 0);
	}
	return 0;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <list>
#include <cmath>

#include <boost/geometry/index/rtree.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>

namespace bg = boost::geometry;
namespace bmpl = boost::mpl;
namespace bgi = bg::index;

// Create a D dimensional point from an array of coordinates
template <size_t D>
struct point_setter {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    point_t& point;
    double *loc;

    point_setter(point_t& point, double *loc) : point(point), loc(loc)
    {}

    template< typename U > void operator()(U i)
    {
        bg::set<i>(point, loc[i]);
    }

};

// Calculate the square of the euclidian distance between two points
template <size_t D>
struct d2_calc {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    const point_t &p1;
    const point_t &p2;
    double &d2;

    d2_calc(const point_t &p1, const point_t &p2, double &d2) : p1(p1), p2(p2), d2(d2)
    {}

    template< typename U > void operator()(U i)
    {
        d2 += pow( bg::get<i>(p1) - bg::get<i>(p2), 2);
    }
};

// Add a scaler to all the coordinates of a point
template <size_t D>
struct add_scalar_to_point {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    point_t &p;
    double c;

    add_scalar_to_point(point_t &p, double c) : p(p), c(c)
    {}

    template< typename U > void operator()(U i)
    {
        double new_coord = bg::get<i>(p) + c;
        bg::set<i>(p, new_coord);
    }

};

template <size_t D>
std::list< std::list<size_t> >
friends_of_friends_rtree(double *data, size_t npts, double linking_length)
{
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;
    typedef std::pair<point_t, size_t> value_t;
    typedef bgi::rtree< value_t, bgi::rstar<16> > tree_t;
    typedef bmpl::range_c<size_t, 0, D> dim_range;

    std::vector< std::pair<point_t, size_t> > points;
    points.reserve(npts);

    for(size_t i = 0 ; i<npts ; ++i) {
        point_t point;
        bmpl::for_each< dim_range >( point_setter<D>(point, data + i*D) );
        points.push_back(std::make_pair(point, i));
    }

    tree_t tree(points.begin(), points.end());

    std::list< std::list< size_t > > groups;

    while( !tree.empty() ) {
        std::list< value_t > to_add;

        // Grab a point from the tree.
        to_add.push_back( *tree.qbegin( bgi::satisfies([](value_t const &){return true;})) );
        tree.remove( to_add.begin(), to_add.end() );

        for( auto it = to_add.begin() ; it != to_add.end() ; ++it ) {
            std::list< value_t >  added;

            // Build box to query
            point_t lower = it->first;
            bmpl::for_each< dim_range >( add_scalar_to_point<D>(lower, -linking_length) );
            point_t upper = it->first;
            bmpl::for_each< dim_range >( add_scalar_to_point<D>(upper, +linking_length));

            bg::model::box< point_t > box( lower, upper );

            auto within_ball = [&it, linking_length](value_t const &v) {
                double d2 = 0.;
                bmpl::for_each< dim_range >( d2_calc<D>(it->first, v.first, d2) );
                return sqrt(d2) < linking_length;
            };

            // Find all points within a linking length of the current point.
            tree.query( bgi::within(box) && bgi::satisfies(within_ball), std::back_inserter(added) );

            // Remove any points we find from the tree as they have been assigned.
            tree.remove( added.begin(), added.end() );

            // Add the found points to the list so we can find their "friends" as well
            to_add.splice(to_add.end(), added);
        }

        std::list< size_t > group;
        for( auto p : to_add ) {
            group.push_back(p.second);
        }
        groups.push_back(group);
    }

    return groups;
}

inline double
dist(double *p1, double *p2, size_t ndim)
{
    double d2 = 0.;
    for(size_t i = 0 ; i < ndim ; ++i) {
        d2 += pow(p1[i] - p2[i], 2);
    }
    return sqrt(d2);
}

// A brute force friends of friends finder without the Rtree accelerator.
std::list< std::list<size_t> >
friends_of_friends_brute(double *data, size_t npts, size_t ndim, double linking_length)
{
    std::cerr << "Using non tree accelerated version" << std::endl;
    typedef std::pair<size_t, double*> Point;

    //Create list of unused points with indices
    std::list<Point> unused;
    std::list< std::list< size_t > > groups;
    for(size_t i=0 ; i<npts ; ++i) {
        unused.push_back(std::make_pair(i, data + i*ndim));
    }

    //If there are unused points try to create a new group
    while( unused.size() > 0 ) {
        std::list<Point> toadd;
        toadd.push_back(unused.front());
        unused.pop_front();

        //Look through all points found in the group and attempt to
        for(auto toadd_it = toadd.begin() ; toadd_it != toadd.end() ; ++toadd_it) {
            auto unused_it = unused.begin();
            while(unused_it != unused.end()) {
                if(dist(unused_it->second, toadd_it->second, ndim) < linking_length) {
                    toadd.push_back(*unused_it);
                    unused.erase(unused_it++);
                } else {
                    ++unused_it;
                }
            }
        }

        std::list<size_t> group;
        for(const auto& p : toadd) {
            group.push_back(p.first);
        }
        groups.push_back(group);
    }

    return groups;
}

std::list< std::list<size_t> >
friends_of_friends(double *data, size_t npts, size_t ndim, double linking_length)
{
    switch(ndim) {
        case 1:
            return friends_of_friends_rtree<1>(data, npts, linking_length);
            break;
        case 2:
            return friends_of_friends_rtree<2>(data, npts, linking_length);
            break;
        case 3:
            return friends_of_friends_rtree<3>(data, npts, linking_length);
            break;
        case 4:
            return friends_of_friends_rtree<4>(data, npts, linking_length);
            break;
        default:
            return friends_of_friends_brute(data, npts, ndim, linking_length);
            break;
    }
}

<commit_msg>Make it compile with modern boost<commit_after>#include <iostream>
#include <vector>
#include <list>
#include <cmath>

#include <boost/geometry/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>

namespace bg = boost::geometry;
namespace bmpl = boost::mpl;
namespace bgi = bg::index;

// Create a D dimensional point from an array of coordinates
template <size_t D>
struct point_setter {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    point_t& point;
    double *loc;

    point_setter(point_t& point, double *loc) : point(point), loc(loc)
    {}

    template< typename U > void operator()(U i)
    {
        bg::set<i>(point, loc[i]);
    }

};

// Calculate the square of the euclidian distance between two points
template <size_t D>
struct d2_calc {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    const point_t &p1;
    const point_t &p2;
    double &d2;

    d2_calc(const point_t &p1, const point_t &p2, double &d2) : p1(p1), p2(p2), d2(d2)
    {}

    template< typename U > void operator()(U i)
    {
        d2 += pow( bg::get<i>(p1) - bg::get<i>(p2), 2);
    }
};

// Add a scaler to all the coordinates of a point
template <size_t D>
struct add_scalar_to_point {
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;

    point_t &p;
    double c;

    add_scalar_to_point(point_t &p, double c) : p(p), c(c)
    {}

    template< typename U > void operator()(U i)
    {
        double new_coord = bg::get<i>(p) + c;
        bg::set<i>(p, new_coord);
    }

};

template <size_t D>
std::list< std::list<size_t> >
friends_of_friends_rtree(double *data, size_t npts, double linking_length)
{
    typedef bg::model::point<double, D, bg::cs::cartesian> point_t;
    typedef std::pair<point_t, size_t> value_t;
    using tree_t =  bgi::rtree< value_t, bgi::linear<16> >;
    typedef bmpl::range_c<size_t, 0, D> dim_range;

    std::vector< std::pair<point_t, size_t> > points;
    points.reserve(npts);

    for(size_t i = 0 ; i<npts ; ++i) {
        point_t point;
        bmpl::for_each< dim_range >( point_setter<D>(point, data + i*D) );
        points.push_back(std::make_pair(point, i));
    }

    tree_t tree(points.begin(), points.end());

    std::list< std::list< size_t > > groups;

    while( !tree.empty() ) {
        std::list< value_t > to_add;

        // Grab a point from the tree.
        to_add.push_back( *tree.qbegin( bgi::satisfies([](value_t const &){return true;})) );
        tree.remove( to_add.begin(), to_add.end() );

        for( auto it = to_add.begin() ; it != to_add.end() ; ++it ) {
            std::list< value_t >  added;

            // Build box to query
            point_t lower = it->first;
            bmpl::for_each< dim_range >( add_scalar_to_point<D>(lower, -linking_length) );
            point_t upper = it->first;
            bmpl::for_each< dim_range >( add_scalar_to_point<D>(upper, +linking_length));

            bg::model::box< point_t > box( lower, upper );

            auto within_ball = [&it, linking_length](value_t const &v) {
                double d2 = 0.;
                bmpl::for_each< dim_range >( d2_calc<D>(it->first, v.first, d2) );
                return sqrt(d2) < linking_length;
            };

            // Find all points within a linking length of the current point.
            tree.query( bgi::within(box) && bgi::satisfies(within_ball), std::back_inserter(added) );

            // Remove any points we find from the tree as they have been assigned.
            tree.remove( added.begin(), added.end() );

            // Add the found points to the list so we can find their "friends" as well
            to_add.splice(to_add.end(), added);
        }

        std::list< size_t > group;
        for( auto p : to_add ) {
            group.push_back(p.second);
        }
        groups.push_back(group);
    }

    return groups;
}

inline double
dist(double *p1, double *p2, size_t ndim)
{
    double d2 = 0.;
    for(size_t i = 0 ; i < ndim ; ++i) {
        d2 += pow(p1[i] - p2[i], 2);
    }
    return sqrt(d2);
}

// A brute force friends of friends finder without the Rtree accelerator.
std::list< std::list<size_t> >
friends_of_friends_brute(double *data, size_t npts, size_t ndim, double linking_length)
{
    std::cerr << "Using non tree accelerated version" << std::endl;
    typedef std::pair<size_t, double*> Point;

    //Create list of unused points with indices
    std::list<Point> unused;
    std::list< std::list< size_t > > groups;
    for(size_t i=0 ; i<npts ; ++i) {
        unused.push_back(std::make_pair(i, data + i*ndim));
    }

    //If there are unused points try to create a new group
    while( unused.size() > 0 ) {
        std::list<Point> toadd;
        toadd.push_back(unused.front());
        unused.pop_front();

        //Look through all points found in the group and attempt to
        for(auto toadd_it = toadd.begin() ; toadd_it != toadd.end() ; ++toadd_it) {
            auto unused_it = unused.begin();
            while(unused_it != unused.end()) {
                if(dist(unused_it->second, toadd_it->second, ndim) < linking_length) {
                    toadd.push_back(*unused_it);
                    unused.erase(unused_it++);
                } else {
                    ++unused_it;
                }
            }
        }

        std::list<size_t> group;
        for(const auto& p : toadd) {
            group.push_back(p.first);
        }
        groups.push_back(group);
    }

    return groups;
}

std::list< std::list<size_t> >
friends_of_friends(double *data, size_t npts, size_t ndim, double linking_length)
{
    switch(ndim) {
        case 1:
            return friends_of_friends_rtree<1>(data, npts, linking_length);
            break;
        case 2:
            return friends_of_friends_rtree<2>(data, npts, linking_length);
            break;
        case 3:
            return friends_of_friends_rtree<3>(data, npts, linking_length);
            break;
        case 4:
            return friends_of_friends_rtree<4>(data, npts, linking_length);
            break;
        default:
            return friends_of_friends_brute(data, npts, ndim, linking_length);
            break;
    }
}

<|endoftext|>