mirror of https://github.com/PurpleI2P/i2pd.git
Jeff Becker
7 years ago
58 changed files with 2114 additions and 903 deletions
@ -1,54 +0,0 @@ |
|||||||
FROM alpine:latest |
|
||||||
|
|
||||||
MAINTAINER Mikal Villa <mikal@sigterm.no> |
|
||||||
|
|
||||||
ENV GIT_BRANCH="master" |
|
||||||
ENV I2PD_PREFIX="/opt/i2pd-${GIT_BRANCH}" |
|
||||||
ENV PATH=${I2PD_PREFIX}/bin:$PATH |
|
||||||
|
|
||||||
ENV GOSU_VERSION=1.7 |
|
||||||
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu" |
|
||||||
|
|
||||||
RUN mkdir /user && adduser -S -h /user i2pd && chown -R i2pd:nobody /user |
|
||||||
|
|
||||||
|
|
||||||
# |
|
||||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the |
|
||||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer. |
|
||||||
# |
|
||||||
|
|
||||||
# 1. install deps, clone and build. |
|
||||||
# 2. strip binaries. |
|
||||||
# 3. Purge all dependencies and other unrelated packages, including build directory. |
|
||||||
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \ |
|
||||||
&& mkdir -p /tmp/build \ |
|
||||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} https://github.com/PurpleI2P/i2pd.git \ |
|
||||||
&& cd i2pd \ |
|
||||||
&& make -j4 \ |
|
||||||
&& mkdir -p ${I2PD_PREFIX}/bin \ |
|
||||||
&& mv i2pd ${I2PD_PREFIX}/bin/ \ |
|
||||||
&& cd ${I2PD_PREFIX}/bin \ |
|
||||||
&& strip i2pd \ |
|
||||||
&& rm -fr /tmp/build && apk --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ |
|
||||||
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \ |
|
||||||
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \ |
|
||||||
libtool g++ gcc pkgconfig |
|
||||||
|
|
||||||
# 2. Adding required libraries to run i2pd to ensure it will run. |
|
||||||
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++ |
|
||||||
|
|
||||||
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu |
|
||||||
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \ |
|
||||||
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu |
|
||||||
|
|
||||||
COPY entrypoint.sh /entrypoint.sh |
|
||||||
|
|
||||||
RUN chmod a+x /entrypoint.sh |
|
||||||
RUN echo "export PATH=${PATH}" >> /etc/profile |
|
||||||
|
|
||||||
VOLUME [ "/var/lib/i2pd" ] |
|
||||||
|
|
||||||
EXPOSE 7070 4444 4447 7656 2827 7654 7650 |
|
||||||
|
|
||||||
ENTRYPOINT [ "/entrypoint.sh" ] |
|
||||||
|
|
@ -1,26 +1,56 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
package="org.purplei2p.i2pd" |
package="org.purplei2p.i2pd" |
||||||
android:versionCode="1" |
android:installLocation="auto" |
||||||
android:versionName="2.18.0" |
android:versionCode="1" |
||||||
android:installLocation="auto"> |
android:versionName="2.18.0"> |
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="25"/> |
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> |
<uses-sdk |
||||||
<uses-permission android:name="android.permission.INTERNET"/> |
android:minSdkVersion="14" |
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> |
android:targetSdkVersion="25" /> |
||||||
<application android:label="@string/app_name" android:allowBackup="true" android:icon="@drawable/icon"> |
|
||||||
<receiver android:name=".NetworkStateChangeReceiver"> |
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
||||||
<intent-filter> |
<uses-permission android:name="android.permission.INTERNET" /> <!-- normal perm, per https://developer.android.com/guide/topics/permissions/normal-permissions.html --> |
||||||
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/> |
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
||||||
</intent-filter> |
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> |
||||||
</receiver> |
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- normal perm --> |
||||||
<activity android:name=".I2PD" |
<application |
||||||
android:label="@string/app_name"> |
android:allowBackup="true" |
||||||
|
android:icon="@drawable/icon" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:theme="@android:style/Theme.Holo.Light.DarkActionBar" |
||||||
|
> |
||||||
|
<receiver android:name=".NetworkStateChangeReceiver"> |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" /> |
||||||
|
</intent-filter> |
||||||
|
</receiver> |
||||||
|
|
||||||
|
<activity |
||||||
|
android:name=".I2PDPermsAskerActivity" |
||||||
|
android:label="@string/app_name"> |
||||||
<intent-filter> |
<intent-filter> |
||||||
<action android:name="android.intent.action.MAIN" /> |
<action android:name="android.intent.action.MAIN" /> |
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" /> |
<category android:name="android.intent.category.LAUNCHER" /> |
||||||
</intent-filter> |
</intent-filter> |
||||||
</activity> |
</activity> |
||||||
<service android:enabled="true" android:name=".ForegroundService"/> |
<activity |
||||||
|
android:name=".I2PDActivity" |
||||||
|
android:label="@string/app_name" /> |
||||||
|
|
||||||
|
<service |
||||||
|
android:name=".ForegroundService" |
||||||
|
android:enabled="true" /> |
||||||
|
|
||||||
|
<activity |
||||||
|
android:name=".I2PDPermsExplanationActivity" |
||||||
|
android:label="@string/title_activity_i2_pdperms_asker_prompt" |
||||||
|
android:parentActivityName=".I2PDPermsAskerActivity"> |
||||||
|
<meta-data |
||||||
|
android:name="android.support.PARENT_ACTIVITY" |
||||||
|
android:value="org.purplei2p.i2pd.I2PDPermsAskerActivity" /> |
||||||
|
</activity> |
||||||
</application> |
</application> |
||||||
</manifest> |
|
||||||
|
</manifest> |
@ -0,0 +1,27 @@ |
|||||||
|
<LinearLayout android:id="@+id/main_layout" |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" |
||||||
|
android:paddingBottom="@dimen/vertical_page_margin" |
||||||
|
android:paddingLeft="@dimen/horizontal_page_margin" |
||||||
|
android:paddingRight="@dimen/horizontal_page_margin" |
||||||
|
android:paddingTop="@dimen/vertical_page_margin" |
||||||
|
tools:context=".I2PDPermsAskerActivity"> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/textview_retry" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginBottom="@dimen/horizontal_page_margin" |
||||||
|
android:visibility="gone" |
||||||
|
/> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/button_request_write_ext_storage_perms" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:text="Retry requesting the SD card write permissions" |
||||||
|
android:visibility="gone"/> |
||||||
|
</LinearLayout> |
@ -0,0 +1,27 @@ |
|||||||
|
<LinearLayout android:id="@+id/layout_prompt" |
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
android:orientation="vertical" |
||||||
|
android:paddingBottom="@dimen/vertical_page_margin" |
||||||
|
android:paddingLeft="@dimen/horizontal_page_margin" |
||||||
|
android:paddingRight="@dimen/horizontal_page_margin" |
||||||
|
android:paddingTop="@dimen/vertical_page_margin" |
||||||
|
tools:context=".I2PDPermsAskerActivity"> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/textview_explanation" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_marginBottom="@dimen/horizontal_page_margin" |
||||||
|
android:text="SD card write access is required to write the keys and other files to the I2PD folder on SD card." |
||||||
|
/> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/button_ok" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:text="OK" |
||||||
|
/> |
||||||
|
</LinearLayout> |
@ -1,11 +1,18 @@ |
|||||||
<?xml version="1.0" encoding="utf-8"?> |
<?xml version="1.0" encoding="utf-8"?> |
||||||
<resources> |
<resources> |
||||||
<string name="app_name">i2pd</string> |
<string name="app_name">i2pd</string> |
||||||
<string name="i2pd_started">i2pd started</string> |
<string name="action_stop">Stop</string> |
||||||
<string name="i2pd_service_started">i2pd service started</string> |
<string name="action_graceful_stop">Graceful Stop</string> |
||||||
<string name="i2pd_service_stopped">i2pd service stopped</string> |
<string name="graceful_stop_is_already_in_progress">Graceful stop is already in progress</string> |
||||||
<string name="action_quit">Quit</string> |
<string name="graceful_stop_is_in_progress">Graceful stop is in progress</string> |
||||||
<string name="action_graceful_quit">Graceful Quit</string> |
<string name="already_stopped">Already stopped</string> |
||||||
<string name="graceful_quit_is_already_in_progress">Graceful quit is already in progress</string> |
<string name="uninitialized">i2pd initializing</string> |
||||||
<string name="graceful_quit_is_in_progress">Graceful quit is in progress</string> |
<string name="starting">i2pd is starting</string> |
||||||
|
<string name="jniLibraryLoaded">i2pd: loaded JNI libraries</string> |
||||||
|
<string name="startedOkay">i2pd started</string> |
||||||
|
<string name="startFailed">i2pd start failed</string> |
||||||
|
<string name="gracefulShutdownInProgress">i2pd: graceful shutdown in progress</string> |
||||||
|
<string name="stopped">i2pd has stopped</string> |
||||||
|
<string name="remaining">remaining</string> |
||||||
|
<string name="title_activity_i2_pdperms_asker_prompt">Prompt</string> |
||||||
</resources> |
</resources> |
||||||
|
@ -0,0 +1,16 @@ |
|||||||
|
<resources> |
||||||
|
|
||||||
|
<!-- Define standard dimensions to comply with Holo-style grids and rhythm. --> |
||||||
|
|
||||||
|
<dimen name="margin_tiny">4dp</dimen> |
||||||
|
<dimen name="margin_small">8dp</dimen> |
||||||
|
<dimen name="margin_medium">16dp</dimen> |
||||||
|
<dimen name="margin_large">32dp</dimen> |
||||||
|
<dimen name="margin_huge">64dp</dimen> |
||||||
|
|
||||||
|
<!-- Semantic definitions --> |
||||||
|
|
||||||
|
<dimen name="horizontal_page_margin">@dimen/margin_medium</dimen> |
||||||
|
<dimen name="vertical_page_margin">@dimen/margin_medium</dimen> |
||||||
|
|
||||||
|
</resources> |
@ -1,245 +0,0 @@ |
|||||||
package org.purplei2p.i2pd; |
|
||||||
|
|
||||||
import java.io.PrintWriter; |
|
||||||
import java.io.StringWriter; |
|
||||||
import java.util.Timer; |
|
||||||
import java.util.TimerTask; |
|
||||||
|
|
||||||
import android.annotation.SuppressLint; |
|
||||||
import android.app.Activity; |
|
||||||
import android.content.ComponentName; |
|
||||||
import android.content.Context; |
|
||||||
import android.content.Intent; |
|
||||||
import android.content.ServiceConnection; |
|
||||||
import android.os.Build; |
|
||||||
import android.os.Bundle; |
|
||||||
import android.os.IBinder; |
|
||||||
import android.util.Log; |
|
||||||
import android.view.Menu; |
|
||||||
import android.view.MenuItem; |
|
||||||
import android.widget.TextView; |
|
||||||
import android.widget.Toast; |
|
||||||
|
|
||||||
public class I2PD extends Activity { |
|
||||||
private static final String TAG = "i2pd"; |
|
||||||
|
|
||||||
private TextView textView; |
|
||||||
|
|
||||||
private final DaemonSingleton daemon = DaemonSingleton.getInstance(); |
|
||||||
|
|
||||||
private DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = |
|
||||||
new DaemonSingleton.StateUpdateListener() { |
|
||||||
|
|
||||||
@Override |
|
||||||
public void daemonStateUpdate() { |
|
||||||
runOnUiThread(new Runnable(){ |
|
||||||
|
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
try { |
|
||||||
if(textView==null)return; |
|
||||||
Throwable tr = daemon.getLastThrowable(); |
|
||||||
if(tr!=null) { |
|
||||||
textView.setText(throwableToString(tr)); |
|
||||||
return; |
|
||||||
} |
|
||||||
DaemonSingleton.State state = daemon.getState(); |
|
||||||
textView.setText(String.valueOf(state)+ |
|
||||||
(DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")); |
|
||||||
} catch (Throwable tr) { |
|
||||||
Log.e(TAG,"error ignored",tr); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
@Override |
|
||||||
public void onCreate(Bundle savedInstanceState) { |
|
||||||
super.onCreate(savedInstanceState); |
|
||||||
|
|
||||||
textView = new TextView(this); |
|
||||||
setContentView(textView); |
|
||||||
DaemonSingleton.getInstance().addStateChangeListener(daemonStateUpdatedListener); |
|
||||||
daemonStateUpdatedListener.daemonStateUpdate(); |
|
||||||
|
|
||||||
//set the app be foreground
|
|
||||||
doBindService(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void onDestroy() { |
|
||||||
super.onDestroy(); |
|
||||||
localDestroy(); |
|
||||||
} |
|
||||||
|
|
||||||
private void localDestroy() { |
|
||||||
textView = null; |
|
||||||
DaemonSingleton.getInstance().removeStateChangeListener(daemonStateUpdatedListener); |
|
||||||
Timer gracefulQuitTimer = getGracefulQuitTimer(); |
|
||||||
if(gracefulQuitTimer!=null) { |
|
||||||
gracefulQuitTimer.cancel(); |
|
||||||
setGracefulQuitTimer(null); |
|
||||||
} |
|
||||||
try{ |
|
||||||
doUnbindService(); |
|
||||||
}catch(Throwable tr){ |
|
||||||
Log.e(TAG, "", tr); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private CharSequence throwableToString(Throwable tr) { |
|
||||||
StringWriter sw = new StringWriter(8192); |
|
||||||
PrintWriter pw = new PrintWriter(sw); |
|
||||||
tr.printStackTrace(pw); |
|
||||||
pw.close(); |
|
||||||
return sw.toString(); |
|
||||||
} |
|
||||||
|
|
||||||
// private LocalService mBoundService;
|
|
||||||
|
|
||||||
private ServiceConnection mConnection = new ServiceConnection() { |
|
||||||
public void onServiceConnected(ComponentName className, IBinder service) { |
|
||||||
// This is called when the connection with the service has been
|
|
||||||
// established, giving us the service object we can use to
|
|
||||||
// interact with the service. Because we have bound to a explicit
|
|
||||||
// service that we know is running in our own process, we can
|
|
||||||
// cast its IBinder to a concrete class and directly access it.
|
|
||||||
// mBoundService = ((LocalService.LocalBinder)service).getService();
|
|
||||||
|
|
||||||
// Tell the user about this for our demo.
|
|
||||||
// Toast.makeText(Binding.this, R.string.local_service_connected,
|
|
||||||
// Toast.LENGTH_SHORT).show();
|
|
||||||
} |
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName className) { |
|
||||||
// This is called when the connection with the service has been
|
|
||||||
// unexpectedly disconnected -- that is, its process crashed.
|
|
||||||
// Because it is running in our same process, we should never
|
|
||||||
// see this happen.
|
|
||||||
// mBoundService = null;
|
|
||||||
// Toast.makeText(Binding.this, R.string.local_service_disconnected,
|
|
||||||
// Toast.LENGTH_SHORT).show();
|
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
private boolean mIsBound; |
|
||||||
|
|
||||||
private void doBindService() { |
|
||||||
// Establish a connection with the service. We use an explicit
|
|
||||||
// class name because we want a specific service implementation that
|
|
||||||
// we know will be running in our own process (and thus won't be
|
|
||||||
// supporting component replacement by other applications).
|
|
||||||
bindService(new Intent(this, |
|
||||||
ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); |
|
||||||
mIsBound = true; |
|
||||||
} |
|
||||||
|
|
||||||
private void doUnbindService() { |
|
||||||
if (mIsBound) { |
|
||||||
// Detach our existing connection.
|
|
||||||
unbindService(mConnection); |
|
||||||
mIsBound = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onCreateOptionsMenu(Menu menu) { |
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
|
||||||
getMenuInflater().inflate(R.menu.options_main, menu); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean onOptionsItemSelected(MenuItem item) { |
|
||||||
// Handle action bar item clicks here. The action bar will
|
|
||||||
// automatically handle clicks on the Home/Up button, so long
|
|
||||||
// as you specify a parent activity in AndroidManifest.xml.
|
|
||||||
int id = item.getItemId(); |
|
||||||
|
|
||||||
switch(id){ |
|
||||||
case R.id.action_quit: |
|
||||||
quit(); |
|
||||||
return true; |
|
||||||
case R.id.action_graceful_quit: |
|
||||||
gracefulQuit(); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item); |
|
||||||
} |
|
||||||
|
|
||||||
@SuppressLint("NewApi") |
|
||||||
private void quit() { |
|
||||||
try { |
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |
|
||||||
finishAndRemoveTask(); |
|
||||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { |
|
||||||
finishAffinity(); |
|
||||||
} else { |
|
||||||
//moveTaskToBack(true);
|
|
||||||
finish(); |
|
||||||
} |
|
||||||
}catch (Throwable tr) { |
|
||||||
Log.e(TAG, "", tr); |
|
||||||
} |
|
||||||
try{ |
|
||||||
daemon.stopDaemon(); |
|
||||||
}catch (Throwable tr) { |
|
||||||
Log.e(TAG, "", tr); |
|
||||||
} |
|
||||||
System.exit(0); |
|
||||||
} |
|
||||||
|
|
||||||
private Timer gracefulQuitTimer; |
|
||||||
private final Object gracefulQuitTimerLock = new Object(); |
|
||||||
private void gracefulQuit() { |
|
||||||
if(getGracefulQuitTimer()!=null){ |
|
||||||
Toast.makeText(this, R.string.graceful_quit_is_already_in_progress, |
|
||||||
Toast.LENGTH_SHORT).show(); |
|
||||||
return; |
|
||||||
} |
|
||||||
Toast.makeText(this, R.string.graceful_quit_is_in_progress, |
|
||||||
Toast.LENGTH_SHORT).show(); |
|
||||||
new Thread(new Runnable(){ |
|
||||||
|
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
try{ |
|
||||||
Log.d(TAG, "grac stopping"); |
|
||||||
if(daemon.isStartedOkay()) { |
|
||||||
daemon.stopAcceptingTunnels(); |
|
||||||
Timer gracefulQuitTimer = new Timer(true); |
|
||||||
setGracefulQuitTimer(gracefulQuitTimer); |
|
||||||
gracefulQuitTimer.schedule(new TimerTask(){ |
|
||||||
|
|
||||||
@Override |
|
||||||
public void run() { |
|
||||||
quit(); |
|
||||||
} |
|
||||||
|
|
||||||
}, 10*60*1000/*milliseconds*/); |
|
||||||
}else{ |
|
||||||
quit(); |
|
||||||
} |
|
||||||
} catch(Throwable tr) { |
|
||||||
Log.e(TAG,"",tr); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
},"gracQuitInit").start(); |
|
||||||
} |
|
||||||
|
|
||||||
private Timer getGracefulQuitTimer() { |
|
||||||
synchronized (gracefulQuitTimerLock) { |
|
||||||
return gracefulQuitTimer; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private void setGracefulQuitTimer(Timer gracefulQuitTimer) { |
|
||||||
synchronized (gracefulQuitTimerLock) { |
|
||||||
this.gracefulQuitTimer = gracefulQuitTimer; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,285 @@ |
|||||||
|
package org.purplei2p.i2pd; |
||||||
|
|
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.io.StringWriter; |
||||||
|
import java.util.Timer; |
||||||
|
import java.util.TimerTask; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.ComponentName; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.ServiceConnection; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.IBinder; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.Menu; |
||||||
|
import android.view.MenuItem; |
||||||
|
import android.widget.TextView; |
||||||
|
import android.widget.Toast; |
||||||
|
|
||||||
|
public class I2PDActivity extends Activity { |
||||||
|
private static final String TAG = "i2pdActvt"; |
||||||
|
public static final int GRACEFUL_DELAY_MILLIS = 10 * 60 * 1000; |
||||||
|
|
||||||
|
private TextView textView; |
||||||
|
|
||||||
|
private static final DaemonSingleton daemon = DaemonSingleton.getInstance(); |
||||||
|
|
||||||
|
private final DaemonSingleton.StateUpdateListener daemonStateUpdatedListener = |
||||||
|
new DaemonSingleton.StateUpdateListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void daemonStateUpdate() { |
||||||
|
runOnUiThread(new Runnable(){ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try { |
||||||
|
if(textView==null)return; |
||||||
|
Throwable tr = daemon.getLastThrowable(); |
||||||
|
if(tr!=null) { |
||||||
|
textView.setText(throwableToString(tr)); |
||||||
|
return; |
||||||
|
} |
||||||
|
DaemonSingleton.State state = daemon.getState(); |
||||||
|
textView.setText( |
||||||
|
String.valueOf(state)+ |
||||||
|
(DaemonSingleton.State.startFailed.equals(state)?": "+daemon.getDaemonStartResult():"")+ |
||||||
|
(DaemonSingleton.State.gracefulShutdownInProgress.equals(state)?": "+formatGraceTimeRemaining()+" "+getText(R.string.remaining):"") |
||||||
|
); |
||||||
|
} catch (Throwable tr) { |
||||||
|
Log.e(TAG,"error ignored",tr); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
private static volatile long graceStartedMillis; |
||||||
|
private static final Object graceStartedMillis_LOCK=new Object(); |
||||||
|
|
||||||
|
private static String formatGraceTimeRemaining() { |
||||||
|
long remainingSeconds; |
||||||
|
synchronized (graceStartedMillis_LOCK){ |
||||||
|
remainingSeconds=Math.round(Math.max(0,graceStartedMillis+GRACEFUL_DELAY_MILLIS-System.currentTimeMillis())/1000.0D); |
||||||
|
} |
||||||
|
long remainingMinutes=(long)Math.floor(remainingSeconds/60.0D); |
||||||
|
long remSec=remainingSeconds-remainingMinutes*60; |
||||||
|
return remainingMinutes+":"+(remSec/10)+remSec%10; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
|
||||||
|
textView = new TextView(this); |
||||||
|
setContentView(textView); |
||||||
|
daemon.addStateChangeListener(daemonStateUpdatedListener); |
||||||
|
daemonStateUpdatedListener.daemonStateUpdate(); |
||||||
|
|
||||||
|
//set the app be foreground
|
||||||
|
doBindService(); |
||||||
|
|
||||||
|
final Timer gracefulQuitTimer = getGracefulQuitTimer(); |
||||||
|
if(gracefulQuitTimer!=null){ |
||||||
|
long gracefulStopAtMillis; |
||||||
|
synchronized (graceStartedMillis_LOCK) { |
||||||
|
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; |
||||||
|
} |
||||||
|
rescheduleGraceStop(gracefulQuitTimer, gracefulStopAtMillis); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDestroy() { |
||||||
|
super.onDestroy(); |
||||||
|
textView = null; |
||||||
|
daemon.removeStateChangeListener(daemonStateUpdatedListener); |
||||||
|
//cancelGracefulStop();
|
||||||
|
try{ |
||||||
|
doUnbindService(); |
||||||
|
}catch(Throwable tr){ |
||||||
|
Log.e(TAG, "", tr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void cancelGracefulStop() { |
||||||
|
Timer gracefulQuitTimer = getGracefulQuitTimer(); |
||||||
|
if(gracefulQuitTimer!=null) { |
||||||
|
gracefulQuitTimer.cancel(); |
||||||
|
setGracefulQuitTimer(null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private CharSequence throwableToString(Throwable tr) { |
||||||
|
StringWriter sw = new StringWriter(8192); |
||||||
|
PrintWriter pw = new PrintWriter(sw); |
||||||
|
tr.printStackTrace(pw); |
||||||
|
pw.close(); |
||||||
|
return sw.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
// private LocalService mBoundService;
|
||||||
|
|
||||||
|
private ServiceConnection mConnection = new ServiceConnection() { |
||||||
|
public void onServiceConnected(ComponentName className, IBinder service) { |
||||||
|
// This is called when the connection with the service has been
|
||||||
|
// established, giving us the service object we can use to
|
||||||
|
// interact with the service. Because we have bound to a explicit
|
||||||
|
// service that we know is running in our own process, we can
|
||||||
|
// cast its IBinder to a concrete class and directly access it.
|
||||||
|
// mBoundService = ((LocalService.LocalBinder)service).getService();
|
||||||
|
|
||||||
|
// Tell the user about this for our demo.
|
||||||
|
// Toast.makeText(Binding.this, R.string.local_service_connected,
|
||||||
|
// Toast.LENGTH_SHORT).show();
|
||||||
|
} |
||||||
|
|
||||||
|
public void onServiceDisconnected(ComponentName className) { |
||||||
|
// This is called when the connection with the service has been
|
||||||
|
// unexpectedly disconnected -- that is, its process crashed.
|
||||||
|
// Because it is running in our same process, we should never
|
||||||
|
// see this happen.
|
||||||
|
// mBoundService = null;
|
||||||
|
// Toast.makeText(Binding.this, R.string.local_service_disconnected,
|
||||||
|
// Toast.LENGTH_SHORT).show();
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
private static volatile boolean mIsBound; |
||||||
|
|
||||||
|
private void doBindService() { |
||||||
|
synchronized (I2PDActivity.class) { |
||||||
|
if (mIsBound) return; |
||||||
|
// Establish a connection with the service. We use an explicit
|
||||||
|
// class name because we want a specific service implementation that
|
||||||
|
// we know will be running in our own process (and thus won't be
|
||||||
|
// supporting component replacement by other applications).
|
||||||
|
bindService(new Intent(this, ForegroundService.class), mConnection, Context.BIND_AUTO_CREATE); |
||||||
|
mIsBound = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void doUnbindService() { |
||||||
|
synchronized (I2PDActivity.class) { |
||||||
|
if (mIsBound) { |
||||||
|
// Detach our existing connection.
|
||||||
|
unbindService(mConnection); |
||||||
|
mIsBound = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onCreateOptionsMenu(Menu menu) { |
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.options_main, menu); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onOptionsItemSelected(MenuItem item) { |
||||||
|
// Handle action bar item clicks here. The action bar will
|
||||||
|
// automatically handle clicks on the Home/Up button, so long
|
||||||
|
// as you specify a parent activity in AndroidManifest.xml.
|
||||||
|
int id = item.getItemId(); |
||||||
|
|
||||||
|
switch(id){ |
||||||
|
case R.id.action_stop: |
||||||
|
i2pdStop(); |
||||||
|
return true; |
||||||
|
case R.id.action_graceful_stop: |
||||||
|
i2pdGracefulStop(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item); |
||||||
|
} |
||||||
|
|
||||||
|
private void i2pdStop() { |
||||||
|
cancelGracefulStop(); |
||||||
|
new Thread(new Runnable(){ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
Log.d(TAG, "stopping"); |
||||||
|
try{ |
||||||
|
daemon.stopDaemon(); |
||||||
|
}catch (Throwable tr) { |
||||||
|
Log.e(TAG, "", tr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
},"stop").start(); |
||||||
|
} |
||||||
|
|
||||||
|
private static volatile Timer gracefulQuitTimer; |
||||||
|
|
||||||
|
private void i2pdGracefulStop() { |
||||||
|
if(daemon.getState()==DaemonSingleton.State.stopped){ |
||||||
|
Toast.makeText(this, R.string.already_stopped, |
||||||
|
Toast.LENGTH_SHORT).show(); |
||||||
|
return; |
||||||
|
} |
||||||
|
if(getGracefulQuitTimer()!=null){ |
||||||
|
Toast.makeText(this, R.string.graceful_stop_is_already_in_progress, |
||||||
|
Toast.LENGTH_SHORT).show(); |
||||||
|
return; |
||||||
|
} |
||||||
|
Toast.makeText(this, R.string.graceful_stop_is_in_progress, |
||||||
|
Toast.LENGTH_SHORT).show(); |
||||||
|
new Thread(new Runnable(){ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
try{ |
||||||
|
Log.d(TAG, "grac stopping"); |
||||||
|
if(daemon.isStartedOkay()) { |
||||||
|
daemon.stopAcceptingTunnels(); |
||||||
|
long gracefulStopAtMillis; |
||||||
|
synchronized (graceStartedMillis_LOCK) { |
||||||
|
graceStartedMillis = System.currentTimeMillis(); |
||||||
|
gracefulStopAtMillis = graceStartedMillis + GRACEFUL_DELAY_MILLIS; |
||||||
|
} |
||||||
|
rescheduleGraceStop(null,gracefulStopAtMillis); |
||||||
|
}else{ |
||||||
|
i2pdStop(); |
||||||
|
} |
||||||
|
} catch(Throwable tr) { |
||||||
|
Log.e(TAG,"",tr); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
},"gracInit").start(); |
||||||
|
} |
||||||
|
|
||||||
|
private void rescheduleGraceStop(Timer gracefulQuitTimerOld, long gracefulStopAtMillis) { |
||||||
|
if(gracefulQuitTimerOld!=null)gracefulQuitTimerOld.cancel(); |
||||||
|
final Timer gracefulQuitTimer = new Timer(true); |
||||||
|
setGracefulQuitTimer(gracefulQuitTimer); |
||||||
|
gracefulQuitTimer.schedule(new TimerTask(){ |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
i2pdStop(); |
||||||
|
} |
||||||
|
|
||||||
|
}, Math.max(0,gracefulStopAtMillis-System.currentTimeMillis())); |
||||||
|
final TimerTask tickerTask = new TimerTask() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
daemonStateUpdatedListener.daemonStateUpdate(); |
||||||
|
} |
||||||
|
}; |
||||||
|
gracefulQuitTimer.scheduleAtFixedRate(tickerTask,0/*start delay*/,1000/*millis period*/); |
||||||
|
} |
||||||
|
|
||||||
|
private static Timer getGracefulQuitTimer() { |
||||||
|
return gracefulQuitTimer; |
||||||
|
} |
||||||
|
|
||||||
|
private static void setGracefulQuitTimer(Timer gracefulQuitTimer) { |
||||||
|
I2PDActivity.gracefulQuitTimer = gracefulQuitTimer; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
package org.purplei2p.i2pd; |
||||||
|
|
||||||
|
import android.Manifest; |
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.pm.PackageManager; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.Button; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
//dangerous perms, per https://developer.android.com/guide/topics/permissions/normal-permissions.html :
|
||||||
|
//android.permission.WRITE_EXTERNAL_STORAGE
|
||||||
|
public class I2PDPermsAskerActivity extends Activity { |
||||||
|
|
||||||
|
private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0; |
||||||
|
|
||||||
|
private Button button_request_write_ext_storage_perms; |
||||||
|
private TextView textview_retry; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
//if less than Android 6, no runtime perms req system present
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 23) { |
||||||
|
startMainActivity(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_perms_asker); |
||||||
|
button_request_write_ext_storage_perms = (Button) findViewById(R.id.button_request_write_ext_storage_perms); |
||||||
|
textview_retry = (TextView) findViewById(R.id.textview_retry); |
||||||
|
|
||||||
|
button_request_write_ext_storage_perms.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
request_write_ext_storage_perms(); |
||||||
|
} |
||||||
|
}); |
||||||
|
request_write_ext_storage_perms(); |
||||||
|
} |
||||||
|
|
||||||
|
private void request_write_ext_storage_perms() { |
||||||
|
|
||||||
|
textview_retry.setVisibility(TextView.GONE); |
||||||
|
button_request_write_ext_storage_perms.setVisibility(Button.GONE); |
||||||
|
|
||||||
|
Method methodCheckPermission; |
||||||
|
Method method_shouldShowRequestPermissionRationale; |
||||||
|
Method method_requestPermissions; |
||||||
|
try { |
||||||
|
methodCheckPermission = getClass().getMethod("checkSelfPermission", String.class); |
||||||
|
method_shouldShowRequestPermissionRationale = |
||||||
|
getClass().getMethod("shouldShowRequestPermissionRationale", String.class); |
||||||
|
method_requestPermissions = |
||||||
|
getClass().getMethod("requestPermissions", String[].class, int.class); |
||||||
|
} catch (NoSuchMethodException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
Integer resultObj; |
||||||
|
try { |
||||||
|
resultObj = (Integer) methodCheckPermission.invoke( |
||||||
|
this, Manifest.permission.WRITE_EXTERNAL_STORAGE); |
||||||
|
} catch (Throwable e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
|
||||||
|
if (resultObj != PackageManager.PERMISSION_GRANTED) { |
||||||
|
|
||||||
|
// Should we show an explanation?
|
||||||
|
Boolean aBoolean; |
||||||
|
try { |
||||||
|
aBoolean = (Boolean) method_shouldShowRequestPermissionRationale.invoke(this, |
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
if (aBoolean) { |
||||||
|
|
||||||
|
// Show an explanation to the user *asynchronously* -- don't block
|
||||||
|
// this thread waiting for the user's response! After the user
|
||||||
|
// sees the explanation, try again to request the permission.
|
||||||
|
|
||||||
|
showExplanation(); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
// No explanation needed, we can request the permission.
|
||||||
|
|
||||||
|
try { |
||||||
|
method_requestPermissions.invoke(this, |
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, |
||||||
|
PERMISSION_WRITE_EXTERNAL_STORAGE); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
} else startMainActivity(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRequestPermissionsResult(int requestCode, |
||||||
|
String permissions[], int[] grantResults) { |
||||||
|
switch (requestCode) { |
||||||
|
case PERMISSION_WRITE_EXTERNAL_STORAGE: { |
||||||
|
// If request is cancelled, the result arrays are empty.
|
||||||
|
if (grantResults.length > 0 |
||||||
|
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) { |
||||||
|
|
||||||
|
// permission was granted, yay! Do the
|
||||||
|
// contacts-related task you need to do.
|
||||||
|
|
||||||
|
startMainActivity(); |
||||||
|
|
||||||
|
} else { |
||||||
|
|
||||||
|
// permission denied, boo! Disable the
|
||||||
|
// functionality that depends on this permission.
|
||||||
|
textview_retry.setText("SD card write permission denied, you need to allow this to continue"); |
||||||
|
textview_retry.setVisibility(TextView.VISIBLE); |
||||||
|
button_request_write_ext_storage_perms.setVisibility(Button.VISIBLE); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// other 'case' lines to check for other
|
||||||
|
// permissions this app might request.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void startMainActivity() { |
||||||
|
startActivity(new Intent(this, I2PDActivity.class)); |
||||||
|
finish(); |
||||||
|
} |
||||||
|
|
||||||
|
private static final int SHOW_EXPLANATION_REQUEST = 1; // The request code
|
||||||
|
private void showExplanation() { |
||||||
|
Intent intent = new Intent(this, I2PDPermsExplanationActivity.class); |
||||||
|
startActivityForResult(intent, SHOW_EXPLANATION_REQUEST); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
// Check which request we're responding to
|
||||||
|
if (requestCode == SHOW_EXPLANATION_REQUEST) { |
||||||
|
// Make sure the request was successful
|
||||||
|
if (resultCode == RESULT_OK) { |
||||||
|
// Request the permission
|
||||||
|
Method method_requestPermissions; |
||||||
|
try { |
||||||
|
method_requestPermissions = |
||||||
|
getClass().getMethod("requestPermissions", String[].class, int.class); |
||||||
|
} catch (NoSuchMethodException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
try { |
||||||
|
method_requestPermissions.invoke(this, |
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, |
||||||
|
PERMISSION_WRITE_EXTERNAL_STORAGE); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} else { |
||||||
|
finish(); //close the app
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package org.purplei2p.i2pd; |
||||||
|
|
||||||
|
import android.app.ActionBar; |
||||||
|
import android.content.Intent; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.app.Activity; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.Button; |
||||||
|
|
||||||
|
public class I2PDPermsExplanationActivity extends Activity { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
setContentView(R.layout.activity_perms_explanation); |
||||||
|
ActionBar actionBar = getActionBar(); |
||||||
|
if(actionBar!=null)actionBar.setHomeButtonEnabled(false); |
||||||
|
Button button_ok = (Button) findViewById(R.id.button_ok); |
||||||
|
button_ok.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
returnFromActivity(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
private void returnFromActivity() { |
||||||
|
Intent data = new Intent(); |
||||||
|
Activity parent = getParent(); |
||||||
|
if (parent == null) { |
||||||
|
setResult(Activity.RESULT_OK, data); |
||||||
|
} else { |
||||||
|
parent.setResult(Activity.RESULT_OK, data); |
||||||
|
} |
||||||
|
finish(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,27 +0,0 @@ |
|||||||
[Unit] |
|
||||||
Description=I2P Router written in C++ |
|
||||||
After=network.target |
|
||||||
|
|
||||||
[Service] |
|
||||||
User=i2pd |
|
||||||
Group=i2pd |
|
||||||
RuntimeDirectory=i2pd |
|
||||||
RuntimeDirectoryMode=0700 |
|
||||||
Type=simple |
|
||||||
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service |
|
||||||
ExecReload=/bin/kill -HUP $MAINPID |
|
||||||
PIDFile=/var/run/i2pd/i2pd.pid |
|
||||||
### Uncomment, if auto restart needed |
|
||||||
#Restart=on-failure |
|
||||||
|
|
||||||
### Use SIGINT for graceful stop daemon. |
|
||||||
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die. |
|
||||||
KillSignal=SIGINT |
|
||||||
TimeoutStopSec=10m |
|
||||||
|
|
||||||
# If you have problems with hanging i2pd, you can try enable this |
|
||||||
#LimitNOFILE=4096 |
|
||||||
PrivateDevices=yes |
|
||||||
|
|
||||||
[Install] |
|
||||||
WantedBy=multi-user.target |
|
@ -0,0 +1,31 @@ |
|||||||
|
[Unit] |
||||||
|
Description=I2P Router written in C++ |
||||||
|
Documentation=man:i2pd(1) https://i2pd.readthedocs.io/en/latest/ |
||||||
|
After=network.target |
||||||
|
|
||||||
|
[Service] |
||||||
|
User=i2pd |
||||||
|
Group=i2pd |
||||||
|
RuntimeDirectory=i2pd |
||||||
|
RuntimeDirectoryMode=0700 |
||||||
|
LogsDirectory=i2pd |
||||||
|
LogsDirectoryMode=0700 |
||||||
|
Type=simple |
||||||
|
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service |
||||||
|
ExecReload=/bin/kill -HUP $MAINPID |
||||||
|
PIDFile=/var/run/i2pd/i2pd.pid |
||||||
|
### Uncomment, if auto restart needed |
||||||
|
#Restart=on-failure |
||||||
|
|
||||||
|
KillSignal=SIGQUIT |
||||||
|
# If you have the patience waiting 10 min on restarting/stopping it, uncomment this. |
||||||
|
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die. |
||||||
|
#KillSignal=SIGINT |
||||||
|
#TimeoutStopSec=10m |
||||||
|
|
||||||
|
# If you have problems with hanging i2pd, you can try enable this |
||||||
|
#LimitNOFILE=4096 |
||||||
|
PrivateDevices=yes |
||||||
|
|
||||||
|
[Install] |
||||||
|
WantedBy=multi-user.target |
@ -0,0 +1,102 @@ |
|||||||
|
%define git_hash %(git rev-parse HEAD | cut -c -7) |
||||||
|
|
||||||
|
Name: i2pd-git |
||||||
|
Version: 2.18.0 |
||||||
|
Release: git%{git_hash}%{?dist} |
||||||
|
Summary: I2P router written in C++ |
||||||
|
Conflicts: i2pd |
||||||
|
|
||||||
|
License: BSD |
||||||
|
URL: https://github.com/PurpleI2P/i2pd |
||||||
|
Source0: https://github.com/PurpleI2P/i2pd/archive/openssl/i2pd-openssl.tar.gz |
||||||
|
|
||||||
|
%if 0%{?rhel} == 7 |
||||||
|
BuildRequires: cmake3 |
||||||
|
%else |
||||||
|
BuildRequires: cmake |
||||||
|
%endif |
||||||
|
|
||||||
|
BuildRequires: chrpath |
||||||
|
BuildRequires: gcc-c++ |
||||||
|
BuildRequires: zlib-devel |
||||||
|
BuildRequires: boost-devel |
||||||
|
BuildRequires: openssl-devel |
||||||
|
BuildRequires: miniupnpc-devel |
||||||
|
BuildRequires: systemd-units |
||||||
|
|
||||||
|
Requires: systemd |
||||||
|
Requires(pre): %{_sbindir}/useradd %{_sbindir}/groupadd |
||||||
|
|
||||||
|
%description |
||||||
|
C++ implementation of I2P. |
||||||
|
|
||||||
|
%prep |
||||||
|
%setup -q |
||||||
|
|
||||||
|
|
||||||
|
%build |
||||||
|
cd build |
||||||
|
%if 0%{?rhel} == 7 |
||||||
|
%cmake3 \ |
||||||
|
-DWITH_LIBRARY=OFF \ |
||||||
|
-DWITH_UPNP=ON \ |
||||||
|
-DWITH_HARDENING=ON \ |
||||||
|
-DBUILD_SHARED_LIBS:BOOL=OFF |
||||||
|
%else |
||||||
|
%cmake \ |
||||||
|
-DWITH_LIBRARY=OFF \ |
||||||
|
-DWITH_UPNP=ON \ |
||||||
|
-DWITH_HARDENING=ON \ |
||||||
|
-DBUILD_SHARED_LIBS:BOOL=OFF |
||||||
|
%endif |
||||||
|
|
||||||
|
make %{?_smp_mflags} |
||||||
|
|
||||||
|
|
||||||
|
%install |
||||||
|
cd build |
||||||
|
chrpath -d i2pd |
||||||
|
install -D -m 755 i2pd %{buildroot}%{_sbindir}/i2pd |
||||||
|
install -D -m 755 %{_builddir}/%{name}-%{version}/contrib/i2pd.conf %{buildroot}%{_sysconfdir}/i2pd/i2pd.conf |
||||||
|
install -D -m 755 %{_builddir}/%{name}-%{version}/contrib/tunnels.conf %{buildroot}%{_sysconfdir}/i2pd/tunnels.conf |
||||||
|
install -d -m 755 %{buildroot}%{_datadir}/i2pd |
||||||
|
%{__cp} -r %{_builddir}/%{name}-%{version}/contrib/certificates/ %{buildroot}%{_datadir}/i2pd/certificates |
||||||
|
install -D -m 644 %{_builddir}/%{name}-%{version}/contrib/rpm/i2pd.service %{buildroot}%{_unitdir}/i2pd.service |
||||||
|
install -d -m 700 %{buildroot}%{_sharedstatedir}/i2pd |
||||||
|
install -d -m 700 %{buildroot}%{_localstatedir}/log/i2pd |
||||||
|
ln -s %{_datadir}/%{name}/certificates %{buildroot}%{_sharedstatedir}/i2pd/certificates |
||||||
|
|
||||||
|
|
||||||
|
%pre |
||||||
|
getent group i2pd >/dev/null || %{_sbindir}/groupadd -r i2pd |
||||||
|
getent passwd i2pd >/dev/null || \ |
||||||
|
%{_sbindir}/useradd -r -g i2pd -s %{_sbindir}/nologin \ |
||||||
|
-d %{_sharedstatedir}/i2pd -c 'I2P Service' i2pd |
||||||
|
|
||||||
|
|
||||||
|
%post |
||||||
|
%systemd_post i2pd.service |
||||||
|
|
||||||
|
|
||||||
|
%preun |
||||||
|
%systemd_preun i2pd.service |
||||||
|
|
||||||
|
|
||||||
|
%postun |
||||||
|
%systemd_postun_with_restart i2pd.service |
||||||
|
|
||||||
|
|
||||||
|
%files |
||||||
|
%doc LICENSE README.md |
||||||
|
%{_sbindir}/i2pd |
||||||
|
%{_datadir}/i2pd/certificates |
||||||
|
%config(noreplace) %{_sysconfdir}/i2pd/* |
||||||
|
/%{_unitdir}/i2pd.service |
||||||
|
%dir %attr(0700,i2pd,i2pd) %{_localstatedir}/log/i2pd |
||||||
|
%dir %attr(0700,i2pd,i2pd) %{_sharedstatedir}/i2pd |
||||||
|
%{_sharedstatedir}/i2pd/certificates |
||||||
|
|
||||||
|
|
||||||
|
%changelog |
||||||
|
* Thu Feb 01 2018 r4sas <r4sas@i2pmail.org> - 2.18.0 |
||||||
|
- Initial i2pd-git based on i2pd 2.18.0-1 spec |
@ -1,27 +0,0 @@ |
|||||||
[Unit] |
|
||||||
Description=I2P Router written in C++ |
|
||||||
After=network.target |
|
||||||
|
|
||||||
[Service] |
|
||||||
User=i2pd |
|
||||||
Group=i2pd |
|
||||||
RuntimeDirectory=i2pd |
|
||||||
RuntimeDirectoryMode=0700 |
|
||||||
Type=simple |
|
||||||
ExecStart=/usr/sbin/i2pd --conf=/etc/i2pd/i2pd.conf --tunconf=/etc/i2pd/tunnels.conf --pidfile=/var/run/i2pd/i2pd.pid --logfile=/var/log/i2pd/i2pd.log --daemon --service |
|
||||||
ExecReload=/bin/kill -HUP $MAINPID |
|
||||||
PIDFile=/var/run/i2pd/i2pd.pid |
|
||||||
### Uncomment, if auto restart needed |
|
||||||
#Restart=on-failure |
|
||||||
|
|
||||||
### Use SIGINT for graceful stop daemon. |
|
||||||
# i2pd stops accepting new tunnels and waits ~10 min while old ones do not die. |
|
||||||
KillSignal=SIGINT |
|
||||||
TimeoutStopSec=10m |
|
||||||
|
|
||||||
# If you have problems with hunging i2pd, you can try enable this |
|
||||||
#LimitNOFILE=4096 |
|
||||||
PrivateDevices=yes |
|
||||||
|
|
||||||
[Install] |
|
||||||
WantedBy=multi-user.target |
|
@ -1,24 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
|
|
||||||
ARGS="" |
|
||||||
if [ "${ENABLE_IPV6}" != "" ]; then |
|
||||||
ARGS="${ARGS} –ipv6" |
|
||||||
fi |
|
||||||
|
|
||||||
if [ "${LOGLEVEL}" != "" ]; then |
|
||||||
ARGS="${ARGS} –loglevel=${LOGLEVEL}" |
|
||||||
fi |
|
||||||
|
|
||||||
if [ "${ENABLE_AUTH}" != "" ]; then |
|
||||||
ARGS="${ARGS} –http.auth" |
|
||||||
fi |
|
||||||
|
|
||||||
|
|
||||||
# To make ports exposeable |
|
||||||
DEFAULT_ARGS=" –http.address=0.0.0.0 –httpproxy.address=0.0.0.0 -socksproxy.address=0.0.0.0 –sam.address=0.0.0.0 –bob.address=0.0.0.0 –i2cp.address=0.0.0.0 –i2pcontrol.port=0.0.0.0 –upnp.enabled=false -service " |
|
||||||
|
|
||||||
mkdir -p /var/lib/i2pd && chown -R i2pd:nobody /var/lib/i2pd && chmod u+rw /var/lib/i2pd |
|
||||||
|
|
||||||
gosu i2pd i2pd $DEFAULT_ARGS $ARGS |
|
||||||
|
|
||||||
|
|
@ -0,0 +1,43 @@ |
|||||||
|
#include "CPU.h" |
||||||
|
#if defined(__x86_64__) || defined(__i386__) |
||||||
|
#include <cpuid.h> |
||||||
|
#endif |
||||||
|
#include "Log.h" |
||||||
|
|
||||||
|
#ifndef bit_AES |
||||||
|
#define bit_AES (1 << 25) |
||||||
|
#endif |
||||||
|
#ifndef bit_AVX |
||||||
|
#define bit_AVX (1 << 28) |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
namespace i2p |
||||||
|
{ |
||||||
|
namespace cpu |
||||||
|
{ |
||||||
|
bool aesni = false; |
||||||
|
bool avx = false; |
||||||
|
|
||||||
|
void Detect() |
||||||
|
{ |
||||||
|
#if defined(__x86_64__) || defined(__i386__) |
||||||
|
int info[4]; |
||||||
|
__cpuid(0, info[0], info[1], info[2], info[3]); |
||||||
|
if (info[0] >= 0x00000001) { |
||||||
|
__cpuid(0x00000001, info[0], info[1], info[2], info[3]); |
||||||
|
aesni = info[2] & bit_AES; // AESNI
|
||||||
|
avx = info[2] & bit_AVX; // AVX
|
||||||
|
} |
||||||
|
#endif |
||||||
|
if(aesni) |
||||||
|
{ |
||||||
|
LogPrint(eLogInfo, "AESNI enabled"); |
||||||
|
} |
||||||
|
if(avx) |
||||||
|
{ |
||||||
|
LogPrint(eLogInfo, "AVX enabled"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef LIBI2PD_CPU_H |
||||||
|
#define LIBI2PD_CPU_H |
||||||
|
|
||||||
|
namespace i2p |
||||||
|
{ |
||||||
|
namespace cpu |
||||||
|
{ |
||||||
|
extern bool aesni; |
||||||
|
extern bool avx; |
||||||
|
|
||||||
|
void Detect(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,81 @@ |
|||||||
|
#ifndef CRYPTO_WORKER_H_ |
||||||
|
#define CRYPTO_WORKER_H_ |
||||||
|
|
||||||
|
#include <condition_variable> |
||||||
|
#include <mutex> |
||||||
|
#include <deque> |
||||||
|
#include <thread> |
||||||
|
#include <vector> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
namespace i2p |
||||||
|
{ |
||||||
|
namespace worker |
||||||
|
{ |
||||||
|
template<typename Caller> |
||||||
|
struct ThreadPool |
||||||
|
{ |
||||||
|
typedef std::function<void(void)> ResultFunc; |
||||||
|
typedef std::function<ResultFunc(void)> WorkFunc; |
||||||
|
typedef std::pair<std::shared_ptr<Caller>, WorkFunc> Job; |
||||||
|
typedef std::mutex mtx_t; |
||||||
|
typedef std::unique_lock<mtx_t> lock_t; |
||||||
|
typedef std::condition_variable cond_t; |
||||||
|
ThreadPool(int workers) |
||||||
|
{ |
||||||
|
stop = false; |
||||||
|
if(workers > 0) |
||||||
|
{ |
||||||
|
while(workers--) |
||||||
|
{ |
||||||
|
threads.emplace_back([this] { |
||||||
|
for (;;) |
||||||
|
{ |
||||||
|
Job job; |
||||||
|
{ |
||||||
|
lock_t lock(this->queue_mutex); |
||||||
|
this->condition.wait( |
||||||
|
lock, [this] { return this->stop || !this->jobs.empty(); }); |
||||||
|
if (this->stop && this->jobs.empty()) return; |
||||||
|
job = std::move(this->jobs.front()); |
||||||
|
this->jobs.pop_front(); |
||||||
|
} |
||||||
|
ResultFunc result = job.second(); |
||||||
|
job.first->GetService().post(result); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
void Offer(const Job & job) |
||||||
|
{ |
||||||
|
{ |
||||||
|
lock_t lock(queue_mutex); |
||||||
|
if (stop) return; |
||||||
|
jobs.emplace_back(job); |
||||||
|
} |
||||||
|
condition.notify_one(); |
||||||
|
} |
||||||
|
|
||||||
|
~ThreadPool() |
||||||
|
{ |
||||||
|
{ |
||||||
|
lock_t lock(queue_mutex); |
||||||
|
stop = true; |
||||||
|
} |
||||||
|
condition.notify_all(); |
||||||
|
for(auto &t: threads) t.join(); |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<std::thread> threads; |
||||||
|
std::deque<Job> jobs; |
||||||
|
mtx_t queue_mutex; |
||||||
|
cond_t condition; |
||||||
|
bool stop; |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#endif |
Loading…
Reference in new issue